home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / addrbook.c next >
C/C++ Source or Header  |  1997-02-24  |  373KB  |  14,123 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: addrbook.c,v 4.348 1996/06/07 18:28:10 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*====================================================================== 
  43.     addrbook.c
  44.     display, browse and edit the address book.
  45.  
  46.     Support routines are in adrbklib.c.
  47.  
  48. The policy for changing the address book is to write it immediately 
  49. after the change is made, so there is no idea of having to save the 
  50. address book.
  51.  ====*/
  52.  
  53.  
  54. #include "headers.h"
  55. #include "adrbklib.h"
  56.  
  57.  
  58. /*
  59.  * We could make every use of an AdrBk_Entry go through a function call
  60.  * like adrbk_get_ae().  Instead, we try to be smart and avoid the extra
  61.  * function calls by knowing when the addrbook entry is still valid, either
  62.  * because we haven't called any functions that could invalidate it or because
  63.  * we have locked it in the cache.  If we do lock it, we need to be careful
  64.  * that it eventually gets unlocked.  That can be done by an explicit
  65.  * adrbk_get_ae(Unlock) call, or it is done implicitly when the address book
  66.  * is written out.  The reason it can get invalidated is that the abe that
  67.  * we get returned to us is just a pointer to a cached addrbook entry, and
  68.  * that entry can be flushed from the cache by other addrbook activity.
  69.  * So we need to be careful to make sure the abe is certain to be valid
  70.  * before using it.
  71.  *
  72.  * Data structures for the display of the address book.  There's one
  73.  * of these structures per line on the screen.
  74.  *
  75.  * Types: Title -- The title line for the different address books.  It has
  76.  *           a ptr to the text of the Title line.
  77.  *    ClickHere -- This is the line that says to click here to
  78.  *                 expand.  It changes types into the individual expanded
  79.  *                 components once it is expanded.  It doesn't have any data
  80.  *                 other than an implicit title.
  81.  * ListClickHere --This is the line that says to click here to
  82.  *                 expand the members of a distribution list.  It changes
  83.  *                 types into the individual expanded ListEnt's (if any)
  84.  *                 when it is expanded.  It has a ptr to an AdrBk_Entry.
  85.  *    ListEmpty -- Line that says this is an empty distribution list.  No data.
  86.  *        Empty -- Line that says this is an empty addressbook.  No data.
  87.  *       Simple -- A single addressbook entry.  It has a ptr to an AdrBk_Entry.
  88.  *                 When it is displayed, the fields are usually:
  89.  *                 <nickname>       <fullname>       <address or another nic>
  90.  *     ListHead -- The head of an address list.  This has a ptr to an
  91.  *           AdrBk_Entry.
  92.  *                 <blank line> followed by
  93.  *                 <nickname>       <fullname>       "DISTRIBUTION LIST:"
  94.  *      ListEnt -- The rest of an address list.  It has a pointer to its
  95.  *           ListHead element and a ptr (other) to this specific address
  96.  *           (not a ptr to another AdrBk_Entry).
  97.  *                 <blank>          <blank>          <address or another nic>
  98.  *         Text -- A ptr to text.  For example, the ----- lines and
  99.  *           whitespace lines.
  100.  *   Beginnning -- The (imaginary) elements before the first real element
  101.  *          End -- The (imaginary) elements after the last real element
  102.  */
  103. typedef enum {DlNotSet, ClickHere, Empty, Title, Simple, ListHead,
  104.         ListClickHere, ListEmpty, ListEnt, Text, Beginning, End} LineType;
  105. /* each line is one of these structures */
  106. typedef struct addrscrn_disp {
  107.     union {
  108.         struct {
  109.             adrbk_cntr_t  ab_element_number; /* which addrbook entry     */
  110.         adrbk_cntr_t  ab_list_offset;    /* which member of the list */
  111.         }addrbook_entry;
  112.         char        *text_ptr;
  113.     }union_to_save_space;
  114.     LineType       type;
  115. } AddrScrn_Disp;
  116. #define txt union_to_save_space.text_ptr
  117. #define elnum union_to_save_space.addrbook_entry.ab_element_number
  118. #define l_offset union_to_save_space.addrbook_entry.ab_list_offset
  119.  
  120. #define entry_is_checked    exp_is_expanded
  121. #define entry_get_next      exp_get_next
  122. #define entry_set_checked   exp_set_expanded
  123. #define entry_unset_checked exp_unset_expanded
  124. #define checked_free        exp_free
  125.  
  126. /*
  127.  * Argument to expand_address and build_address_internal is a BuildTo,
  128.  * which is either a char * address or an AdrBk_Entry * (if we have already
  129.  * looked it up in an addrbook).
  130.  */
  131. typedef enum {Str, Abe} Build_To_Arg_Type;
  132. typedef struct build_to {
  133.     Build_To_Arg_Type type;
  134.     union {
  135.     char        *str;  /* normal looking address string */
  136.     AdrBk_Entry *abe;  /* addrbook entry */
  137.     }arg;
  138. } BuildTo;
  139.  
  140. typedef enum {BP_Unset, BP_To, BP_Lcc} WhoSetUs;
  141. typedef struct builder_private {
  142.     WhoSetUs who;
  143.     int      cksumlen;
  144.     long     cksumval;
  145. } BuilderPrivate;
  146.  
  147. typedef enum {DlcNotSet,
  148.           DlcTitleBlankTop,
  149.           DlcTitleDashTop,
  150.           DlcTitle,
  151.           DlcTitleDashBottom,
  152.           DlcTitleBlankBottom,
  153.           DlcClickHere,
  154.           DlcEmpty,
  155.           DlcNoPermission,
  156.           DlcSimple,
  157.           DlcListHead,
  158.           DlcListClickHere,
  159.           DlcListEmpty,
  160.           DlcListEnt,
  161.           DlcListBlankTop,
  162.           DlcListBlankBottom,
  163.           DlcBeginning,
  164.           DlcOneBeforeBeginning,
  165.           DlcTwoBeforeBeginning,
  166.           DlcEnd} DlCacheType;
  167.  
  168. typedef enum {Initialize, FirstEntry, LastEntry, ArbitraryStartingPoint,
  169.           DoneWithCache, FlushDlcFromCache, Lookup} DlMgrOps;
  170. typedef enum {Warp, DontWarp} HyperType;
  171. /*
  172.  * The DlCacheTypes are the types that a dlcache element can be labeled.
  173.  * The idea is that there needs to be enough information in the single
  174.  * cache element by itself so that you can figure out what the next and
  175.  * previous dl rows are by just knowing this one row.
  176.  *
  177.  * If there is only one addrbook, there are no DlcTitle* lines, and no
  178.  * DlcClickHere lines.  The first line will be a DlcListHead or a DlcSimple.
  179.  * If more than one addrbook, each has a title.  All but the first have
  180.  * a DlcTitleBlankTop blank line before the title, and all of them have
  181.  * the other 4 types of DlcTitle type lines.  An unexpanded book is a
  182.  * DlcClickHere, an Empty book is a DlcEmpty.  Each DlcSimple is just a
  183.  * single line.  Each list is a DlcListHead and some number of DlcListEnts
  184.  * if there are any list members.  If there are none, there is a DlcListEmpty.
  185.  * If the list isn't expanded, instead of a variable number of DlcListEnts
  186.  * there is one DlcListClickHere after the DlcListHead.  Two lists are
  187.  * separated by a DlcListBlankBottom belonging to the first list.  A list
  188.  * followed or preceded by a DlcSimple address row has a
  189.  * DlcListBlank(Top or Bottom) separating it from the DlcSimple.  A DlcTitle
  190.  * and a list are separated by a DlcTitleBlank*, not a DlcListBlank*.
  191.  * Each cache element has an adrbk number associated with it.  All of the
  192.  * DlcTitle* rows, including the DlcTitleBlankTop, go with the adrbk they
  193.  * are the title for.  DlcListBlankTop's have the elnum of the list
  194.  * they're above.  DlcListBlankBottom's have the elnum of the list
  195.  * they're below.  Above the top row of the display list is a type
  196.  * DlcBeginning row.  Below the bottom row of the display list is a
  197.  * type DlcEnd row.
  198.  */
  199. typedef struct dl_cache {
  200.     long         global_row; /* disp_list row number */
  201.     adrbk_cntr_t dlcelnum;   /* which elnum from that addrbook */
  202.     adrbk_cntr_t dlcoffset;  /* offset in a list, only for ListEnt rows */
  203.     short        adrbk_num;  /* which address book we're related to */
  204.     DlCacheType  type;       /* type of this row */
  205.     AddrScrn_Disp dl;         /* the actual dl that goes with this row */
  206. } DL_CACHE_S;
  207.  
  208. typedef enum {Nickname, Fullname, Addr, Filecopy, Comment, Notused,
  209.           Def, WhenNoAddrDisplayed, Checkbox} ColumnType;
  210. /*
  211.  * Users can customize the addrbook display, so this tells us which data
  212.  * is in a particular column and how wide the column is.  There is an
  213.  * array of these per addrbook, of length NFIELDS (number of possible cols).
  214.  */
  215. typedef struct column_description {
  216.     ColumnType type;
  217.     WidthType  wtype;
  218.     int        req_width; /* requested width (for fixed and percent types) */
  219.     int        width;     /* actual width to use */
  220.     int        old_width;
  221. } COL_S;
  222.  
  223. typedef enum {LocalPersonal, LocalGlobal} AddrBookType;
  224. typedef enum {TotallyClosed, /* hash tables not even set up yet               */
  225.           Closed,     /* data not read in, no display list                */
  226.           NoDisplay,  /* data is accessible, no display list              */
  227.           HalfOpen,   /* data not accessible, initial display list is set */
  228.           Open        /* data is accessible and display list is set       */
  229.          } OpenStatus;
  230. /*
  231.  * There is one of these per addressbook.
  232.  */
  233. typedef struct peraddrbook {
  234.     AddrBookType        type;
  235.     AccessType          access;
  236.     OpenStatus          ostatus;
  237.     char               *nickname,
  238.                *filename;
  239.     AdrBk              *address_book;        /* the address book handle */
  240.     int                 gave_parse_warnings;
  241.     COL_S               disp_form[NFIELDS];  /* display format */
  242.     int            nick_is_displayed;   /* these are for convenient, */
  243.     int            full_is_displayed;   /* fast access.  Could get   */
  244.     int            addr_is_displayed;   /* same info from disp_form. */
  245.     int            fcc_is_displayed;
  246.     int            comment_is_displayed;
  247. } PerAddrBook;
  248.  
  249. /*
  250.  * Just one of these.  This keeps track of the current state of
  251.  * the screen and which addressbook we're looking at.  It is really just
  252.  * global data (accessed only from this file).  It's all in one structure so
  253.  * that it's easier to recognize as global.
  254.  */
  255. typedef struct addrscreenstate {
  256.     PerAddrBook   *adrbks;       /* array of addrbooks                    */
  257.     int           initialized,  /* have we done at least simple init?    */
  258.                    n_addrbk,     /* how many addrbooks are there          */
  259.                    how_many_personals, /* how many of those are personal? */
  260.                    cur,          /* current addrbook                      */
  261.                    cur_row,      /* currently selected line               */
  262.                    old_cur_row,  /* previously selected line              */
  263.                    l_p_page;     /* lines per (screen) page               */
  264.     long           top_ent;      /* index in disp_list of top entry on screen */
  265.     int            ro_warning;   /* whether or not to give warning        */
  266.     int            checkboxes;   /* whether or not to display checkboxes  */
  267.     int            no_op_possbl; /* user can't do anything with current conf */
  268. #ifdef    _WINDOWS
  269.     long       last_ent;     /* index of last known entry          */
  270. #endif
  271. } AddrScrState;
  272.  
  273. static AddrScrState as;
  274.  
  275. /*
  276.  * AddrBookScreen is the maintenance screen, all the others are selection
  277.  * screens.  Those that end in Com are called from the pico HeaderEditor,
  278.  * either while in the composer or while editing an address book entry.
  279.  * SelectManyNicks returns an array of nicknames.  SelectAddrLccCom and
  280.  * SelectNicksCom return a comma-separated list of nicknames.  SelectNickTake,
  281.  * SelectNickCom, and SelectNick all return a single nickname.  SelectAddrCom
  282.  * returns a comma-separated list of addresses.  SelectAddr, SelectAddrTake,
  283.  * and SelectAddrNoFullCom return a single address (which can actually be a
  284.  * comma-separated list but is sort of intended to be a single address).
  285.  * SelectAddrTake and SelectAddrNoFullCom eliminate the fullname field
  286.  * before returning the address.  The ones that returns multiple nicknames
  287.  * or multiple addresses all allow ListMode.  They are SelectAddrCom,
  288.  * SelectAddrLccCom, SelectNicksCom, and SelectManyNicks (which automatically
  289.  * starts in ListMode).
  290.  */
  291. typedef enum {AddrBookScreen,       /* maintenance screen                     */
  292.           SelectAddrCom,       /* returns list of addresses              */
  293.           SelectAddrLccCom,       /* returns list of nicknames of lists     */
  294.           SelectNicksCom,       /* just like SelectAddrLccCom, but allows
  295.                       selecting simple *and* list entries    */
  296.           SelectAddr,       /* returns single expanded entry          */
  297.           SelectAddrTake,       /* same as above but fullname is stripped */
  298.           SelectAddrNoFullCom, /* same as above but from composer        */
  299.           SelectNick,       /* returns single nickname                */
  300.           SelectNickTake,       /* Same as SelectNick but different help  */
  301.           SelectNickCom,       /* Same as SelectNick but from composer   */
  302.           SelectManyNicks       /* auto ListMode, returns nicks in array  */
  303.          } AddrBookArg;
  304.  
  305. typedef struct save_state_struct {
  306.     AddrScrState *savep;
  307.     OpenStatus   *stp;
  308.     DL_CACHE_S   *dlc_to_warp_to;
  309. } SAVE_STATE_S;
  310.  
  311. /*
  312.  * Information used to paint and maintain a line on the TakeAddr screen
  313.  */
  314. typedef struct takeaddr_line {
  315.     int                      checked;  /* addr is selected                     */
  316.     int                      skip_it;  /* skip this one                        */
  317.     int                      print;    /* for printing only                    */
  318.     int                      frwrded;  /* forwarded from another pine          */
  319.     char                 *strvalue; /* alloc'd value string                 */
  320.     ADDRESS              *addr;     /* original ADDRESS this line came from */
  321.     char                 *nickname; /* The first TA may carry this extra    */
  322.     char                 *fullname; /*   information                        */
  323.     char                 *fcc;
  324.     char                 *comment;
  325.     struct takeaddr_line *next, *prev;
  326. } TA_S;
  327.  
  328. typedef enum {ListMode, SingleMode} TakeAddrScreenMode;
  329.  
  330. typedef struct takeaddress_screen {
  331.     TakeAddrScreenMode mode;
  332.     TA_S              *current,
  333.                       *top_line;
  334. } TA_SCREEN_S;
  335.  
  336. static TA_SCREEN_S *ta_screen;
  337.  
  338. /*
  339.  * Jump back to this location if we discover that one of the open addrbooks
  340.  * has been changed by some other process.
  341.  *
  342.  * The trouble_filename variable is usually NULL (no trouble) but may be
  343.  * set if adrbklib detected trouble in an addrbook.lu file.  In that case,
  344.  * trouble_filename will be set to the name of the addressbook
  345.  * with the problem.  It is used to force a rebuild of the .lu file.
  346.  */
  347. jmp_buf addrbook_changed_unexpectedly;
  348. char   *trouble_filename;
  349.  
  350.  
  351. void           ab_compose_to_addr PROTO((long));
  352. void           ab_export PROTO((long, int));
  353. void           ab_forward PROTO((struct pine *, long));
  354. void           ab_goto_folder PROTO((int));
  355. void           ab_print PROTO((void));
  356. void           ab_resize PROTO(());
  357. long           ab_whereis PROTO((int *, int));
  358. void           add_abook_entry PROTO((TA_S *, char *, char *, char *,
  359.                                 char *, int));
  360. int            add_addresses_to_talist PROTO((struct pine *, long, char *,
  361.                             TA_S **, ADDRESS *, int));
  362. void           add_forced_entries PROTO((AdrBk *));
  363. char          *addr_book PROTO((AddrBookArg, char *, char ***));
  364. char          *addr_book_change_list PROTO((char **));
  365. int            addr_book_delete PROTO((AdrBk *, int, long, int *));
  366. void           addr_book_manynicks PROTO((char ***));
  367. char          *addr_book_nick_for_edit PROTO((void));
  368. char          *addr_book_seladdr PROTO((void));
  369. char          *addr_book_seladdr_nofull PROTO((void));
  370. char          *addr_book_selnick PROTO((void));
  371. char          *addr_book_takeaddr PROTO((void));
  372. char          *addr_lookup PROTO((char *, int *, int));
  373. AdrBk_Entry   *addr_to_abe PROTO((ADDRESS *));
  374. AccessType     adrbk_access PROTO((char *));
  375. AdrBk_Entry   *adrbk_lookup_with_opens_by_nick PROTO((char *, int, int *, int));
  376. int            adrbk_num_from_lineno PROTO((long));
  377. AdrBk_Entry   *ae PROTO((long));
  378. int            any_addrs_avail PROTO((long));
  379. int            build_address_internal PROTO((BuildTo, char **, char **,
  380.                         char **, int *, char **, int, int));
  381. int            calculate_field_widths PROTO((void));
  382. void           cancel_warning PROTO((int, char *));
  383. PerAddrBook   *check_for_addrbook PROTO((char *));
  384. void           clickable_warning PROTO((long));
  385. ADDRESS       *copyaddrlist PROTO((ADDRESS *));
  386. int            cur_addr_book PROTO((void));
  387. char          *decode_fullname_of_addrstring PROTO((char *, int));
  388. DL_CACHE_S    *dlc_from_listmem PROTO((AdrBk *, a_c_arg_t, char *));
  389. DL_CACHE_S    *dlc_next PROTO((DL_CACHE_S *, DL_CACHE_S *));
  390. DL_CACHE_S    *dlc_prev PROTO((DL_CACHE_S *, DL_CACHE_S *));
  391. AddrScrn_Disp *dlist PROTO((long));
  392. DL_CACHE_S    *dlc_mgr PROTO((long, DlMgrOps, DL_CACHE_S *));
  393. int            dlcs_from_same_abe PROTO((DL_CACHE_S *, DL_CACHE_S *));
  394. void           display_book PROTO((int, int, int, int, Pos *));
  395. void           done_with_dlc_cache PROTO((void));
  396. int            dup_addrs PROTO((ADDRESS *, ADDRESS *));
  397. void           dump_a_dlc_to_debug PROTO((char *, DL_CACHE_S *));
  398. void           dump_some_debugging PROTO((char *));
  399. void           edit_entry PROTO((AdrBk *, AdrBk_Entry *, a_c_arg_t, Tag,
  400.                                 int, int *));
  401. int            edit_nickname PROTO((AdrBk *, AddrScrn_Disp *, int, char *,
  402.                         char *, HelpType, int, int));
  403. int            eliminate_dups_and_us PROTO((TA_S *));
  404. int            eliminate_dups_but_not_us PROTO((TA_S *));
  405. int            eliminate_dups_and_maybe_us PROTO((TA_S *, int));
  406. void           empty_warning PROTO((long));
  407. char          *encode_fullname_of_addrstring PROTO((char *, char *));
  408. void           end_adrbks PROTO((void));
  409. int            entry_is_clickable PROTO((long));
  410. int            est_size PROTO((ADDRESS *));
  411. ADDRESS       *expand_address PROTO((BuildTo, char *, char *, int *, char **,
  412.                      int *, char **, char **, int, int));
  413. void           expand_addrs_for_pico PROTO((struct headerentry *));
  414. void           fill_in_dl_field PROTO((DL_CACHE_S *));
  415. TA_S          *fill_in_ta PROTO((TA_S **, ADDRESS *, int, char *));
  416. int            find_in_book PROTO((long, char *, long *, int *));
  417. TA_S          *first_checked PROTO((TA_S *));
  418. long           first_line PROTO((long));
  419. long           first_selectable_line PROTO((long));
  420. TA_S          *first_sel_taline PROTO((TA_S *));
  421. TA_S          *last_sel_taline PROTO((TA_S *));
  422. TA_S          *first_taline PROTO((TA_S *));
  423. TA_S          *whereis_taline PROTO((TA_S *));
  424. void           flush_dlc_from_cache PROTO((DL_CACHE_S *));
  425. void           free_cache_array PROTO((DL_CACHE_S **, int));
  426. void           free_list PROTO((char ***));
  427. void           free_taline PROTO((TA_S **));
  428. int            funny_compare_dlcs PROTO((DL_CACHE_S *, DL_CACHE_S *));
  429. DL_CACHE_S    *get_bottom_dl_of_adrbk PROTO((int, DL_CACHE_S *));
  430. DL_CACHE_S    *get_dlc PROTO((long));
  431. DL_CACHE_S    *get_first_dl_of_adrbk PROTO((int, DL_CACHE_S *));
  432. DL_CACHE_S    *get_global_bottom_dlc PROTO((DL_CACHE_S *));
  433. DL_CACHE_S    *get_global_top_dlc PROTO((DL_CACHE_S *));
  434. int            get_line_of_message PROTO((STORE_S *, char *, int));
  435. char          *getaltcharset PROTO ((char *, char **, char **));
  436. int            grab_addrs_from_body PROTO((MAILSTREAM *, long, BODY *,
  437.                                     TA_S **));
  438. void           init_ab_if_needed PROTO((void));
  439. int            init_addrbooks PROTO((OpenStatus, int, int, int));
  440. void           init_abook PROTO((PerAddrBook *, OpenStatus));
  441. void           init_disp_form PROTO((PerAddrBook *, char **, int));
  442. void           initialize_dlc_cache PROTO((void));
  443. void           internal_take PROTO((AdrBk *, long));
  444. int            is_addr PROTO((long));
  445. int            is_empty PROTO((long));
  446. int            is_talist_of_one PROTO((TA_S *));
  447. int            line_is_selectable PROTO((long));
  448. char         **list_of_checked PROTO((TA_S *));
  449. char          *listmem PROTO((long));
  450. adrbk_cntr_t   listmem_count_from_abe PROTO((AdrBk_Entry *));
  451. char          *listmem_from_dl PROTO((AdrBk *, AddrScrn_Disp *));
  452. int            matching_dlcs PROTO((DL_CACHE_S *, DL_CACHE_S *));
  453. TA_S          *new_taline PROTO((TA_S **));
  454. int            next_selectable_line PROTO((long, long *));
  455. TA_S          *next_sel_taline PROTO((TA_S *));
  456. TA_S          *next_taline PROTO((TA_S *));
  457. int            nickname_check PROTO((char *, char **));
  458. void           no_tabs_warning PROTO((void));
  459. int            our_build_address PROTO((BuildTo, char **, char **, char **,
  460.                                     int, int));
  461. void           paint_line PROTO((int, long, int, Pos *));
  462. void           parse_format PROTO((char *, COL_S *));
  463. char          *pico_cancel_for_adrbk_edit PROTO((void));
  464. char          *pico_cancel_for_adrbk_take PROTO((void));
  465. char          *pico_cancelexit_for_adrbk PROTO((char *));
  466. char          *pico_sendexit_for_adrbk PROTO((void));
  467. int            prev_selectable_line PROTO((long, long *));
  468. TA_S          *pre_sel_taline PROTO((TA_S *));
  469. TA_S          *pre_taline PROTO((TA_S *));
  470. int            process_special_abook_attachments PROTO((MAILSTREAM *, long,
  471.                             BODY *, BODY *, TA_S **));
  472. void           readonly_warning PROTO((int, char *));
  473. void           redraw_addr_screen PROTO((void));
  474. void           restore_state PROTO((SAVE_STATE_S *));
  475. void           rfc822_write_address_decode PROTO((char *, ADDRESS *, char **));
  476. ADDRESS       *abe_to_address PROTO((AdrBk_Entry *, AddrScrn_Disp *,
  477.                      AdrBk *, int *));
  478. char          *abe_to_nick_or_addr_string PROTO((AdrBk_Entry *,
  479.                          AddrScrn_Disp *, AdrBk *));
  480. void           save_state PROTO((SAVE_STATE_S *));
  481. int            search_book PROTO((long, int, long *, int *, int *));
  482. int            search_in_one_line PROTO((AddrScrn_Disp *, AdrBk_Entry *, char *,
  483.                                 char *));
  484. PerAddrBook   *setup_for_addrbook_add PROTO((SAVE_STATE_S *, int));
  485. void           strip_personal_quotes PROTO((ADDRESS *));
  486. int            ta_take_marked_addrs PROTO((int, TA_S *, int));
  487. int            ta_take_single_addr PROTO((TA_S *, int));
  488. int            ta_mark_all PROTO((TA_S *));
  489. int            ta_unmark_all PROTO((TA_S *));
  490. void           take_to_addrbooks PROTO((char **, char *, char *, char *, char *,
  491.                                 char *, int));
  492. void           take_to_addrbooks_frontend PROTO((char **, char *, char *,
  493.                          char *, char *, char *, int));
  494. void           takeaddr_screen PROTO((struct pine *, TA_S *, int,
  495.                     TakeAddrScreenMode));
  496. void           takeaddr_screen_redrawer_list PROTO((void));
  497. void           takeaddr_screen_redrawer_single PROTO((void));
  498. int            update_takeaddr_screen PROTO((struct pine *, TA_S *,
  499.                             TA_SCREEN_S *, Pos *));
  500. PerAddrBook   *use_this_addrbook PROTO((int));
  501. int            verify_addr PROTO((char *, char **, char **, BUILDER_ARG *));
  502. int            verify_nick PROTO((char *, char **, char **, BUILDER_ARG *));
  503. char          *view_message_for_pico PROTO((void));
  504. void           warp_to_dlc PROTO((DL_CACHE_S *, long));
  505. void           warp_to_beginning PROTO((void));
  506. void           warp_to_end PROTO((void));
  507. #ifdef    _WINDOWS
  508. int           addr_scroll_up PROTO((long));
  509. int           addr_scroll_down PROTO((long));
  510. int           addr_scroll_to_pos PROTO((long));
  511. int           addr_scroll_callback PROTO((int, long));
  512. #endif
  513.  
  514.  
  515. #define CLICKHERE       "[ Select Here to See Expanded List ]"
  516. #define NO_PERMISSION   "[ Permission Denied ]"
  517. #define EMPTY           "[ Empty ]"
  518. #define READONLY        "(ReadOnly)"
  519. #define NOACCESS        "(Un-readable)"
  520. #define DISTLIST        "DISTRIBUTION LIST:"
  521.  
  522. #define MAX_FCC     MAX_ADDRESS
  523. #define MAX_COMMENT (10000)
  524.  
  525. #define DING    1
  526. #define NO_DING 0
  527.  
  528. /*
  529.  * These constants are supposed to be suitable for use as longs where the longs
  530.  * are representing a line number or message number.
  531.  * These constants aren't suitable for use with type adrbk_cntr_t.  There is
  532.  * a constant called NO_NEXT in adrbklib.h which you probably want for that.
  533.  */
  534. #define NO_LINE         (2147483645L)
  535. #define CHANGED_CURRENT (NO_LINE + 1L)
  536.  
  537.  
  538. /*
  539.  * Make sure addrbooks are minimally initialized.
  540.  */
  541. void
  542. init_ab_if_needed()
  543. {
  544.     dprint(9, (debugfile, "- init_ab_if_needed -\n"));
  545.  
  546.     if(!as.initialized)
  547.       (void)init_addrbooks(Closed, 0, 0, 1);
  548. }
  549.  
  550.  
  551. /*
  552.  * Sets everything up to get started.
  553.  *
  554.  * Args: want_status      -- The desired OpenStatus for all addrbooks.
  555.  *       reset_to_top_or_bot -- Forget about the old location and put cursor
  556.  *                           at top.  If value is 1, reset to top, if value
  557.  *                           is -1, reset to bottom, else, don't reset.
  558.  *       open_if_only_one -- If want_status is HalfOpen and there is only
  559.  *                           one addressbook, then promote want_status to Open
  560.  *       ro_warning       -- Issue ReadOnly warning if set, also sets global
  561.  *
  562.  * Return: number of addrbooks.
  563.  */
  564. int
  565. init_addrbooks(want_status, reset_to_top_or_bot, open_if_only_one, ro_warning)
  566.     OpenStatus want_status;
  567.     int        reset_to_top_or_bot,
  568.            open_if_only_one,
  569.            ro_warning;
  570. {
  571.     register PerAddrBook *pab;
  572.     char *q, **t;
  573.     long line;
  574.  
  575.     dprint(2, (debugfile, "-- init_addrbooks(%s, %d, %d, %d) --\n",
  576.             want_status==Open ?
  577.                 "Open" :
  578.                 want_status==HalfOpen ?
  579.                     "HalfOpen" :
  580.                     want_status==NoDisplay ?
  581.                         "NoDisplay" :
  582.                         "Closed",
  583.             reset_to_top_or_bot, open_if_only_one, ro_warning));
  584.  
  585.     as.l_p_page = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)
  586.                            - HEADER_ROWS(ps_global);
  587.  
  588.     /* already been initialized */
  589.     if(as.n_addrbk){
  590.     int i;
  591.  
  592.     as.ro_warning = ro_warning;
  593.  
  594.     /*
  595.      * Special case.  If there is only one addressbook we start the
  596.      * user out with that open, just like we did when there was always
  597.      * only one addressbook.
  598.      *
  599.      * Also start out open if expanded-view-of-addrbooks feature is set.
  600.      */
  601.     if(want_status == HalfOpen &&
  602.        ((open_if_only_one && as.n_addrbk == 1) ||
  603.         F_ON(F_EXPANDED_ADDRBOOKS, ps_global)))
  604.         want_status = Open;
  605.  
  606.     /* open to correct state */
  607.     for(i = 0; i < as.n_addrbk; i++)
  608.       init_abook(&as.adrbks[i], want_status);
  609.  
  610.     if(reset_to_top_or_bot == 1)
  611.       warp_to_beginning();
  612.     else if(reset_to_top_or_bot == -1)
  613.       warp_to_end();
  614.  
  615.     if(reset_to_top_or_bot == 1){
  616.         as.top_ent     = 0L;
  617.         line           = first_selectable_line(0L);
  618.         if(line == NO_LINE)
  619.           as.cur_row = 0L;
  620.         else
  621.           as.cur_row = line;
  622.  
  623.         if(as.cur_row >= as.l_p_page)
  624.           as.top_ent += (as.cur_row - as.l_p_page + 1);
  625.  
  626.         as.old_cur_row = as.cur_row;
  627.     }
  628.     else if(reset_to_top_or_bot == -1){
  629.         as.top_ent     = first_line(0L - (long)as.l_p_page/2L);
  630.         line           = first_selectable_line(as.top_ent);
  631.         if(line == NO_LINE)
  632.           as.cur_row = 0L;
  633.         else
  634.           as.cur_row = line - as.top_ent;
  635.  
  636.         if(as.cur_row >= as.l_p_page)
  637.           as.top_ent += (as.cur_row - as.l_p_page + 1);
  638.  
  639.         as.old_cur_row = as.cur_row;
  640.     }
  641.  
  642.     dprint(9, (debugfile, "init_addrbooks: already initialized: %d books\n",
  643.                     as.n_addrbk));
  644.         return(as.n_addrbk);
  645.     }
  646.  
  647.     /* there are no addrbooks */
  648.     if(as.initialized && !as.n_addrbk)
  649.       return 0;
  650.  
  651.     as.initialized = 1;
  652.  
  653.     as.ro_warning = ro_warning;
  654.     as.no_op_possbl = 0;
  655.  
  656.  
  657.     if((!ps_global->VAR_GLOB_ADDRBOOK ||
  658.         !ps_global->VAR_GLOB_ADDRBOOK[0] ||
  659.         !ps_global->VAR_GLOB_ADDRBOOK[0][0]) &&
  660.        (!ps_global->VAR_ADDRESSBOOK ||
  661.         !ps_global->VAR_ADDRESSBOOK[0] ||
  662.         !ps_global->VAR_ADDRESSBOOK[0][0]))
  663.     return 0;
  664.     
  665.  
  666.     /* count addressbooks */
  667.     as.how_many_personals = 0;
  668.     if(ps_global->VAR_ADDRESSBOOK &&
  669.        ps_global->VAR_ADDRESSBOOK[0] &&
  670.        ps_global->VAR_ADDRESSBOOK[0][0])
  671.     for(t = ps_global->VAR_ADDRESSBOOK; *t != NULL; t++)
  672.       as.how_many_personals++;
  673.  
  674.     as.n_addrbk = as.how_many_personals;
  675.     if(ps_global->VAR_GLOB_ADDRBOOK &&
  676.        ps_global->VAR_GLOB_ADDRBOOK[0] &&
  677.        ps_global->VAR_GLOB_ADDRBOOK[0][0])
  678.     for(t = ps_global->VAR_GLOB_ADDRBOOK; *t != NULL; t++)
  679.       as.n_addrbk++;
  680.  
  681.     if(want_status == HalfOpen &&
  682.        ((open_if_only_one && as.n_addrbk == 1) ||
  683.     F_ON(F_EXPANDED_ADDRBOOKS, ps_global)))
  684.     want_status = Open;
  685.  
  686.     as.cur      = 0;
  687.     as.top_ent = 0L;
  688.     as.cur_row = 0L;
  689.  
  690.     /*
  691.      * allocate array of PerAddrBooks
  692.      * (we don't give this up until we exit Pine, but it's small)
  693.      */
  694.     as.adrbks       = (PerAddrBook *)fs_get(as.n_addrbk * sizeof(PerAddrBook));
  695.     memset((void *)as.adrbks, 0, as.n_addrbk * sizeof(PerAddrBook));
  696.  
  697.     /* init PerAddrBook data */
  698.     for(as.cur = 0; as.cur < as.n_addrbk; as.cur++){
  699.     char *nickname = NULL,
  700.          *filename = NULL;
  701.  
  702.     if(as.cur < as.how_many_personals)
  703.       q = ps_global->VAR_ADDRESSBOOK[as.cur];
  704.     else
  705.       q = ps_global->VAR_GLOB_ADDRBOOK[as.cur - as.how_many_personals];
  706.  
  707.     pab = &as.adrbks[as.cur];
  708.     
  709.     /* Parse entry for optional nickname and filename */
  710.     get_pair(q, &nickname, &filename, 0);
  711.  
  712.         strcpy(tmp_20k_buf, filename);
  713.     fs_give((void **)&filename);
  714.  
  715.         filename = tmp_20k_buf;
  716.     if(nickname == NULL)
  717.       pab->nickname = cpystr(filename);
  718.     else
  719.       pab->nickname = nickname;
  720.  
  721.     if(*filename == '~')
  722.       fnexpand(filename, 20000);
  723.  
  724.     if(is_absolute_path(filename)){
  725.         pab->filename = cpystr(filename); /* fully qualified */
  726.     }
  727.     else{
  728.         char book_path[MAXPATH+1];
  729.         char *lc = last_cmpnt(ps_global->pinerc);
  730.  
  731.         book_path[0] = '\0';
  732.         if(lc != NULL){
  733.         strncpy(book_path, ps_global->pinerc, lc - ps_global->pinerc);
  734.         book_path[lc - ps_global->pinerc] = '\0';
  735.         }
  736.  
  737.         strcat(book_path, filename);
  738.         pab->filename = cpystr(book_path);
  739.     }
  740.  
  741.     if(as.cur < as.how_many_personals)
  742.       pab->type  = LocalPersonal;
  743.     else
  744.       pab->type  = LocalGlobal;
  745.  
  746.     pab->access = adrbk_access(pab->filename);
  747.  
  748.     /* global address books are forced readonly */
  749.     if(as.cur >= as.how_many_personals && pab->access != NoAccess)
  750.       pab->access = ReadOnly;
  751.  
  752.     pab->ostatus  = TotallyClosed;
  753.  
  754.     if(as.ro_warning &&
  755.         open_if_only_one &&
  756.         as.n_addrbk == 1 &&
  757.         want_status == Open){
  758.  
  759.         if(pab->access == ReadOnly)
  760.           readonly_warning(NO_DING, NULL);
  761.         else if(pab->access == NoAccess)
  762.           q_status_message(SM_ORDER, 0, 4,
  763.             "AddressBook not accessible, permission denied");
  764.     }
  765.  
  766.     /*
  767.      * and remember that the memset above initializes everything
  768.      * else to 0
  769.      */
  770.  
  771.     init_abook(pab, want_status);
  772.     }
  773.  
  774.     /*
  775.      * Have to reset_to_top in this case since this is the first open,
  776.      * regardless of the value of the argument, since these values haven't been
  777.      * set before here.
  778.      */
  779.     warp_to_beginning();
  780.     as.top_ent     = 0L;
  781.     line           = first_selectable_line(0L);
  782.     if(line == NO_LINE)
  783.       as.cur_row = 0L;
  784.     else
  785.       as.cur_row = line;
  786.  
  787.     if(as.cur_row >= as.l_p_page)
  788.       as.top_ent += (as.cur_row - as.l_p_page + 1);
  789.  
  790.     as.old_cur_row = as.cur_row;
  791.  
  792.     return(as.n_addrbk);
  793. }
  794.  
  795.  
  796. /*
  797.  * Might help a little to debug problems.
  798.  */
  799. void
  800. dump_some_debugging(message)
  801.     char *message;
  802. {
  803. #ifdef DEBUG
  804.     dprint(2, (debugfile, "- dump_some_debugging(%s) -\n", message));
  805.     dprint(2, (debugfile, "initialized %d n_addrbk %d cur_row %d\n",
  806.     as.initialized, as.n_addrbk, as.cur_row));
  807.     dprint(2, (debugfile, "top_ent %ld ro_warning %d no_op_possbl %d\n",
  808.     as.top_ent, as.ro_warning, as.no_op_possbl));
  809. #endif /* DEBUG */
  810. }
  811.  
  812.  
  813. void
  814. init_disp_form(pab, list, addrbook_num)
  815. PerAddrBook *pab;
  816. char       **list;
  817. int         addrbook_num;
  818. {
  819.     char *last_one;
  820.     int   column = 0;
  821.  
  822.     memset((void *)pab->disp_form, 0, sizeof(COL_S));
  823.     pab->disp_form[1].wtype = WeCalculate; /* so we don't get false AllAuto */
  824.  
  825.     if(as.checkboxes){
  826.     pab->disp_form[column].wtype     = Fixed;
  827.     pab->disp_form[column].req_width = 3;
  828.     pab->disp_form[column++].type    = Checkbox;
  829.     }
  830.  
  831.     /* if custom format is specified */
  832.     if(list && list[0] && list[0][0]){
  833.     /* find the one for addrbook_num */
  834.     for(last_one = *list;
  835.         *list != NULL && addrbook_num;
  836.         addrbook_num--,list++)
  837.       last_one = *list;
  838.  
  839.     /* If not enough to go around, last one repeats */
  840.     if(*list == NULL)
  841.       parse_format(last_one, &(pab->disp_form[column]));
  842.     else
  843.       parse_format(*list, &(pab->disp_form[column]));
  844.     
  845.     if(column == 0 && pab->disp_form[0].wtype == AllAuto)
  846.       pab->disp_form[1].wtype = AllAuto;
  847.     }
  848.     else{  /* default */
  849.     /* If 2nd wtype is AllAuto, the widths are calculated old way */
  850.     pab->disp_form[1].wtype   = AllAuto;
  851.  
  852.     pab->disp_form[column++].type  = Nickname;
  853.     pab->disp_form[column++].type  = Fullname;
  854.     pab->disp_form[column++].type  = Addr;
  855.     /* Fill in rest */
  856.     while(column < NFIELDS)
  857.       pab->disp_form[column++].type = Notused;
  858.     }
  859. }
  860.  
  861.  
  862. struct parse_tokens {
  863.     char *name;
  864.     ColumnType ctype;
  865. };
  866.  
  867. struct parse_tokens ptokens[] = {
  868.     {"NICKNAME", Nickname},
  869.     {"FULLNAME", Fullname},
  870.     {"ADDRESS",  Addr},
  871.     {"FCC",      Filecopy},
  872.     {"COMMENT",  Comment},
  873.     {"DEFAULT",  Def},
  874.     {NULL,       Notused}
  875. };
  876. /*
  877.  * Parse format_str and fill in disp_form structure based on what's there.
  878.  *
  879.  * Args: format_str -- The format string from pinerc.
  880.  *        disp_form -- This is where we fill in the answer.
  881.  *
  882.  * The format string consists of special tokens which give the order of
  883.  * the columns to be displayed.  The possible tokens are NICKNAME,
  884.  * FULLNAME, ADDRESS, FCC, COMMENT.  If a token is followed by
  885.  * parens with an integer inside (FULLNAME(16)) then that means we
  886.  * make that variable that many characters wide.  If it is a percentage, we
  887.  * allocate that percentage of the columns to that variable.  If no
  888.  * parens, that means we calculate it for the user.  The tokens are
  889.  * delimited by white space.  A token of DEFAULT means to calculate the
  890.  * whole thing as we would if no spec was given.  This makes it possible
  891.  * to specify default for one addrbook and something special for another.
  892.  */
  893. void
  894. parse_format(format_str, disp_form)
  895. char  *format_str;
  896. COL_S *disp_form;
  897. {
  898.     int column = 0;
  899.     char *p, *q;
  900.     struct parse_tokens *pt;
  901.     int nicknames, fullnames, addresses, not_allauto;
  902.     int warnings = 0;
  903.  
  904.     p = format_str;
  905.     while(p && *p && column < NFIELDS){
  906.     /* skip leading white space for next word */
  907.     while(p && *p && isspace((unsigned char)(*p)))
  908.       p++;
  909.     
  910.     /* look for the ptoken this word matches */
  911.     for(pt = ptokens; pt->name; pt++)
  912.         if(!struncmp(pt->name, p, strlen(pt->name)))
  913.           break;
  914.     
  915.     /* ignore unrecognized word */
  916.     if(!pt->name){
  917.         char *r;
  918.  
  919.         if((r=strindex(p, SPACE)) != NULL)
  920.           *r = '\0';
  921.  
  922.         dprint(2, (debugfile, "parse_format: ignoring unrecognized word \"%s\" in address-book-formats\n", p));
  923.         q_status_message1(SM_ORDER, warnings++==0 ? 1 : 0, 4,
  924.         "Ignoring unrecognized word \"%s\" in address-book-formats", p);
  925.         /* put back space */
  926.         if(r)
  927.           *r = SPACE;
  928.  
  929.         /* skip unrecognized word */
  930.         while(p && *p && !isspace((unsigned char)(*p)))
  931.           p++;
  932.  
  933.         continue;
  934.     }
  935.  
  936.     disp_form[column].type = pt->ctype;
  937.  
  938.     /* skip over name and look for parens */
  939.     p += strlen(pt->name);
  940.     if(*p == '('){
  941.         p++;
  942.         q = p;
  943.         while(p && *p && isdigit((unsigned char)*p))
  944.           p++;
  945.         
  946.         if(p && *p && *p == ')' && p > q){
  947.         disp_form[column].wtype = Fixed;
  948.         disp_form[column].req_width = atoi(q);
  949.         }
  950.         else if(p && *p && *p == '%' && p > q){
  951.         disp_form[column].wtype = Percent;
  952.         disp_form[column].req_width = atoi(q);
  953.         }
  954.         else{
  955.         disp_form[column].wtype = WeCalculate;
  956.         if(disp_form[column].type == Nickname)
  957.           disp_form[column].req_width = 8;
  958.         else
  959.           disp_form[column].req_width = 3;
  960.         }
  961.     }
  962.     else{
  963.         disp_form[column].wtype     = WeCalculate;
  964.         if(disp_form[column].type == Nickname)
  965.           disp_form[column].req_width = 8;
  966.         else
  967.           disp_form[column].req_width = 3;
  968.     }
  969.  
  970.     if(disp_form[column].type == Def){
  971.         /* If any type is _DEFAULT_, the widths are calculated old way */
  972. assign_default:
  973.         column = 0;
  974.         disp_form[1].wtype  = AllAuto;
  975.  
  976.         disp_form[column++].type = Nickname;
  977.         disp_form[column++].type = Fullname;
  978.         disp_form[column++].type = Addr;
  979.         /* Fill in rest */
  980.         while(column < NFIELDS)
  981.           disp_form[column++].type = Notused;
  982.  
  983.         return;
  984.     }
  985.  
  986.     column++;
  987.     /* skip text at end of word */
  988.     while(p && *p && !isspace((unsigned char)(*p)))
  989.       p++;
  990.     }
  991.  
  992.     if(column == 0){
  993.     q_status_message(SM_ORDER, 0, 4,
  994.     "address-book-formats has no recognizable words, using default format");
  995.     goto assign_default;
  996.     }
  997.  
  998.     /* Fill in rest */
  999.     while(column < NFIELDS)
  1000.       disp_form[column++].type = Notused;
  1001.  
  1002.     /* check to see if user is just re-ordering default fields */
  1003.     nicknames = 0;
  1004.     fullnames = 0;
  1005.     addresses = 0;
  1006.     not_allauto = 0;
  1007.     for(column = 0; column < NFIELDS; column++){
  1008.     if(disp_form[column].type != Notused
  1009.        && disp_form[column].wtype != WeCalculate)
  1010.       not_allauto++;
  1011.  
  1012.     switch(disp_form[column].type){
  1013.       case Nickname:
  1014.         nicknames++;
  1015.         break;
  1016.  
  1017.       case Fullname:
  1018.         fullnames++;
  1019.         break;
  1020.  
  1021.       case Addr:
  1022.         addresses++;
  1023.         break;
  1024.  
  1025.       case Filecopy:
  1026.       case Comment:
  1027.         not_allauto++;
  1028.         break;
  1029.     }
  1030.     }
  1031.  
  1032.     /*
  1033.      * Special case: if there is no address field specified, we put in
  1034.      * a special field called WhenNoAddrDisplayed, which causes list
  1035.      * entries to be displayable in all cases.
  1036.      */
  1037.     if(!addresses){
  1038.     for(column = 0; column < NFIELDS; column++)
  1039.       if(disp_form[column].type == Notused)
  1040.         break;
  1041.     
  1042.     if(column < NFIELDS){
  1043.         disp_form[column].type  = WhenNoAddrDisplayed;
  1044.         disp_form[column].wtype = Special;
  1045.     }
  1046.     }
  1047.  
  1048.     if(nicknames == 1 && fullnames == 1 && addresses == 1 && not_allauto == 0)
  1049.       disp_form[0].wtype = AllAuto; /* set to do default widths */
  1050. }
  1051.  
  1052.  
  1053. void
  1054. dump_a_dlc_to_debug(message, dlc)
  1055.     char *message;
  1056.     DL_CACHE_S *dlc;
  1057. {
  1058. #ifdef DEBUG
  1059.     char type[20];
  1060.  
  1061.     switch(dlc->type){
  1062.       case DlcTitleBlankTop:
  1063.     (void)strcpy(type, "TitleBlankTop");
  1064.     break;
  1065.       case DlcTitleDashTop:
  1066.     (void)strcpy(type, "TitleDashTop");
  1067.     break;
  1068.       case DlcTitle:
  1069.     (void)strcpy(type, "Title");
  1070.     break;
  1071.       case DlcTitleDashBottom:
  1072.     (void)strcpy(type, "TitleDashBottom");
  1073.     break;
  1074.       case DlcTitleBlankBottom:
  1075.     (void)strcpy(type, "TitleBlankBottom");
  1076.     break;
  1077.       case DlcClickHere:
  1078.     (void)strcpy(type, "ClickHere");
  1079.     break;
  1080.       case DlcListClickHere:
  1081.     (void)strcpy(type, "ListClickHere");
  1082.     break;
  1083.       case DlcListEmpty:
  1084.     (void)strcpy(type, "ListEmpty");
  1085.     break;
  1086.       case DlcEmpty:
  1087.     (void)strcpy(type, "Empty");
  1088.     break;
  1089.       case DlcNoPermission:
  1090.     (void)strcpy(type, "NoPermission");
  1091.     break;
  1092.       case DlcSimple:
  1093.     (void)strcpy(type, "Simple");
  1094.     break;
  1095.       case DlcListHead:
  1096.     (void)strcpy(type, "ListHead");
  1097.     break;
  1098.       case DlcListEnt:
  1099.     (void)strcpy(type, "ListEnt");
  1100.     break;
  1101.       case DlcListBlankTop:
  1102.     (void)strcpy(type, "ListBlankTop");
  1103.     break;
  1104.       case DlcListBlankBottom:
  1105.     (void)strcpy(type, "ListBlankBottom");
  1106.     break;
  1107.       case DlcNotSet:
  1108.     (void)strcpy(type, "NotSet");
  1109.     break;
  1110.       case DlcBeginning:
  1111.     (void)strcpy(type, "Beginning");
  1112.     break;
  1113.       case DlcOneBeforeBeginning:
  1114.     (void)strcpy(type, "OneBeforeBeginning");
  1115.     break;
  1116.       case DlcTwoBeforeBeginning:
  1117.     (void)strcpy(type, "TwoBeforeBeginning");
  1118.     break;
  1119.       case DlcEnd:
  1120.     (void)strcpy(type, "End");
  1121.     break;
  1122.     }
  1123.  
  1124.     dprint(2, (debugfile,
  1125.     "%s: type %s adrbk_num %d\n",
  1126.     message, type, (int)dlc->adrbk_num));
  1127.     dprint(2, (debugfile,
  1128.     "   global_row %ld dlcelnum %ld dlcoffset %ld\n",
  1129.     (long)dlc->global_row, (long)dlc->dlcelnum, (long)dlc->dlcoffset));
  1130.  
  1131. #endif /* DEBUG */
  1132. }
  1133.  
  1134.  
  1135. /*
  1136.  * Create addrbook_file.lu lookup file and exit.  This is for
  1137.  * use as a stand-alone creator of .lu files.
  1138.  */
  1139. void
  1140. just_update_lookup_file(addrbook_file, sort_rule_descrip)
  1141.     char *addrbook_file;
  1142.     char *sort_rule_descrip;
  1143. {
  1144.     int        i;
  1145.     int        sort_rule;
  1146.     NAMEVAL_S *v;
  1147.     AdrBk     *ab;
  1148.     char       warning[800];
  1149.  
  1150.  
  1151.     sort_rule = -1;
  1152.     for(i = 0; v = ab_sort_rules(i); i++){
  1153.        if(!strucmp(sort_rule_descrip, v->name)){
  1154.        sort_rule = v->value;
  1155.        break;
  1156.        }
  1157.     }
  1158.  
  1159.     if(sort_rule == -1){
  1160.     fprintf(stderr, "Sort rule %s unknown\n", sort_rule_descrip);
  1161.     exit(-1);
  1162.     }
  1163.  
  1164.     warning[0] = '\0';
  1165.     ab = adrbk_open(addrbook_file, NULL, warning, sort_rule, 1, 1);
  1166.     if(ab == NULL){
  1167.     if(*warning)
  1168.       fprintf(stderr, "%s: %s\n", addrbook_file, warning);
  1169.     else
  1170.       fprintf(stderr, "%s: %s\n",
  1171.         addrbook_file, error_description(errno));
  1172.  
  1173.     exit(-1);
  1174.     }
  1175.  
  1176.     if(!adrbk_is_in_sort_order(ab, 1)){
  1177.     adrbk_set_nominal_cachesize(ab, (long)adrbk_count(ab));
  1178.     (void)adrbk_sort(ab, (a_c_arg_t)0, (adrbk_cntr_t *)NULL, 1);
  1179.     }
  1180.  
  1181.     exit(0);
  1182. }
  1183.  
  1184.  
  1185. /*
  1186.  * Something was changed in options screen, so need to start over.
  1187.  */
  1188. void
  1189. addrbook_reset()
  1190. {
  1191.     dprint(2, (debugfile, "- addrbook_reset -\n"));
  1192.     completely_done_with_adrbks();
  1193. }
  1194.  
  1195.  
  1196. /*
  1197.  * Returns type of access allowed on this addrbook.
  1198.  */
  1199. AccessType
  1200. adrbk_access(filename)
  1201.     char *filename;
  1202. {
  1203.     char       fbuf[501];
  1204.     AccessType access = NoExists;
  1205.  
  1206.     build_path(fbuf, is_absolute_path(filename) ? NULL : ps_global->home_dir,
  1207.            filename);
  1208.  
  1209. #if defined(DOS) || defined(OS2)
  1210.     /*
  1211.      * Microsoft networking causes some access calls to do a DNS query (!!!)
  1212.      * when it is turned on.  In particular, if there is a / in the filename,
  1213.      * this seems to happen.  So, just return does not exist in that case.
  1214.      */
  1215.     if(strindex(fbuf, '/') != NULL){
  1216.     dprint(2, (debugfile, "\"/\" not allowed in addrbook name\n"));
  1217.     return NoAccess;
  1218.     }
  1219. #else /* !DOS */
  1220.     /* also prevent backslash in non-DOS addrbook names */
  1221.     if(strindex(fbuf, '\\') != NULL){
  1222.     dprint(2, (debugfile, "\"\\\" not allowed in addrbook name\n"));
  1223.     return NoAccess;
  1224.     }
  1225. #endif /* !DOS */
  1226.  
  1227.     if(can_access(fbuf, ACCESS_EXISTS) == 0){
  1228.     if(can_access(fbuf, EDIT_ACCESS) == 0){
  1229.         char *dir, *p;
  1230.  
  1231.         dir = ".";
  1232.         if(p = last_cmpnt(fbuf)){
  1233.         *--p = '\0';
  1234.         dir  = *fbuf ? fbuf : "/";
  1235.         }
  1236.  
  1237. #if    defined(DOS) || defined(OS2)
  1238.         /*
  1239.          * If the dir has become a drive letter and : (e.g. "c:")
  1240.          * then append a "\".  The library function access() in the
  1241.          * win 16 version of MSC seems to require this.
  1242.          */
  1243.         if(isalpha((unsigned char) *dir)
  1244.            && *(dir+1) == ':' && *(dir+2) == '\0'){
  1245.         *(dir+2) = '\\';
  1246.         *(dir+3) = '\0';
  1247.         }
  1248. #endif    /* DOS || OS2 */
  1249.  
  1250.         /*
  1251.          * Even if we can edit the address book file itself, we aren't
  1252.          * going to be able to change it unless we can also write in
  1253.          * the directory that contains it (because we write into a
  1254.          * temp file and then rename).
  1255.          */
  1256.         if(can_access(dir, EDIT_ACCESS) == 0)
  1257.           access = ReadWrite;
  1258.         else
  1259.           access = ReadOnly;
  1260.     }
  1261.     else if(can_access(fbuf, READ_ACCESS) == 0)
  1262.       access = ReadOnly;
  1263.     else
  1264.       access = NoAccess;
  1265.     }
  1266.     
  1267.     return(access);
  1268. }
  1269.  
  1270.  
  1271. /*
  1272.  * Returns the index of the current address book.
  1273.  */
  1274. int
  1275. cur_addr_book()
  1276. {
  1277.     return(adrbk_num_from_lineno(as.top_ent + as.cur_row));
  1278. }
  1279.  
  1280.  
  1281. /*
  1282.  * Returns the index of the current address book.
  1283.  */
  1284. int
  1285. adrbk_num_from_lineno(lineno)
  1286.     long lineno;
  1287. {
  1288.     DL_CACHE_S *dlc;
  1289.  
  1290.     dlc = get_dlc(lineno);
  1291.  
  1292.     return(dlc->adrbk_num);
  1293. }
  1294.  
  1295.  
  1296. void
  1297. end_adrbks()
  1298. {
  1299.     int i;
  1300.  
  1301.     dprint(2, (debugfile, "- end_adrbks -\n"));
  1302.  
  1303.     if(!as.initialized)
  1304.       return;
  1305.  
  1306.     for(i = 0; i < as.n_addrbk; i++)
  1307.       init_abook(&as.adrbks[i], Closed);
  1308. }
  1309.  
  1310.  
  1311. /*
  1312.  * Free and close everything.
  1313.  */
  1314. void
  1315. completely_done_with_adrbks()
  1316. {
  1317.     register PerAddrBook *pab;
  1318.     int i;
  1319.  
  1320.     dprint(2, (debugfile, "- completely_done_with_adrbks -\n"));
  1321.  
  1322.     if(!as.initialized)
  1323.       return;
  1324.  
  1325.     for(i = 0; i < as.n_addrbk; i++)
  1326.       init_abook(&as.adrbks[i], TotallyClosed);
  1327.  
  1328.     for(i = 0; i < as.n_addrbk; i++){
  1329.     pab = &as.adrbks[i];
  1330.  
  1331.     if(pab->filename)
  1332.       fs_give((void **)&pab->filename);
  1333.  
  1334.     if(pab->nickname)
  1335.       fs_give((void **)&pab->nickname);
  1336.     }
  1337.  
  1338.     done_with_dlc_cache();
  1339.  
  1340.     if(as.adrbks)
  1341.       fs_give((void **)&as.adrbks);
  1342.  
  1343.     as.n_addrbk    = 0;
  1344.     as.initialized = 0;
  1345. }
  1346.  
  1347.  
  1348. /*
  1349.  * Save the screen state and the Open or Closed status of the addrbooks.
  1350.  */
  1351. void
  1352. save_state(state)
  1353.     SAVE_STATE_S *state;
  1354. {
  1355.     int                 i;
  1356.     DL_CACHE_S         *dlc;
  1357.  
  1358.     dprint(9, (debugfile, "- save_state -\n"));
  1359.  
  1360.     if(as.n_addrbk == 0)
  1361.       return;
  1362.  
  1363.     /* allocate space for saving the screen structure and save it */
  1364.     state->savep    = (AddrScrState *)fs_get(sizeof(AddrScrState));
  1365.     *(state->savep) = as; /* copy the struct */
  1366.  
  1367.  
  1368.     /* allocate space for saving the ostatus for each addrbook */
  1369.     state->stp = (OpenStatus *)fs_get(as.n_addrbk * sizeof(OpenStatus));
  1370.  
  1371.     for(i = 0; i < as.n_addrbk; i++)
  1372.       (state->stp)[i] = as.adrbks[i].ostatus;
  1373.  
  1374.  
  1375.     state->dlc_to_warp_to = (DL_CACHE_S *)fs_get(sizeof(DL_CACHE_S));
  1376.     dlc = get_dlc(as.top_ent + as.cur_row);
  1377.     *(state->dlc_to_warp_to) = *dlc; /* copy the struct */
  1378. }
  1379.  
  1380.  
  1381. /*
  1382.  * Restore the state.
  1383.  *
  1384.  * Side effect: Flushes addrbook entry cache entries so they need to be
  1385.  * re-fetched afterwords.  This only applies to entries obtained since
  1386.  * the call to save_state.
  1387.  * Also flushes all dlc cache entries, so dlist calls need to be repeated.
  1388.  */
  1389. void
  1390. restore_state(state)
  1391.     SAVE_STATE_S *state;
  1392. {
  1393.     int i;
  1394.  
  1395.     dprint(9, (debugfile, "- restore_state -\n"));
  1396.  
  1397.     if(as.n_addrbk == 0)
  1398.       return;
  1399.  
  1400.     as = *(state->savep);  /* put back cur_row and all that */
  1401.  
  1402.     /* restore addressbook OpenStatus to what it was before */
  1403.     for(i = 0; i < as.n_addrbk; i++){
  1404.     init_disp_form(&as.adrbks[i], ps_global->VAR_ABOOK_FORMATS, i);
  1405.     init_abook(&as.adrbks[i], (state->stp)[i]);
  1406.     }
  1407.  
  1408.     /*
  1409.      * jump cache back to where we were
  1410.      */
  1411.     warp_to_dlc(state->dlc_to_warp_to, as.top_ent+as.cur_row);
  1412.  
  1413.     fs_give((void **)&state->dlc_to_warp_to);
  1414.     fs_give((void **)&state->stp);
  1415.     fs_give((void **)&state->savep);
  1416. }
  1417.  
  1418.  
  1419. /*
  1420.  * Initialize or re-initialize this address book.
  1421.  *
  1422.  *  Args: pab        -- the PerAddrBook ptr
  1423.  *       want_status -- desired OpenStatus for this address book
  1424.  */
  1425. void
  1426. init_abook(pab, want_status)
  1427.     PerAddrBook *pab;
  1428.     OpenStatus   want_status;
  1429. {
  1430.     register OpenStatus new_status;
  1431.  
  1432.     dprint(7, (debugfile, "- init_abook -\n"));
  1433.     dprint(7, (debugfile,
  1434.         "    addrbook nickname = %s filename = %s want ostatus %s\n",
  1435.         pab->nickname ? pab->nickname : "<null>",
  1436.         pab->filename ? pab->filename : "<null>",
  1437.         want_status==Open ?
  1438.                 "Open" :
  1439.                 want_status==HalfOpen ?
  1440.                     "HalfOpen" :
  1441.                     want_status==NoDisplay ?
  1442.                         "NoDisplay" :
  1443.                         want_status==Closed ?
  1444.                         "Closed" :
  1445.                         "TotallyClosed"));
  1446.     dprint(7, (debugfile, "    ostatus was %s, want %s\n",
  1447.         pab->ostatus==Open ?
  1448.                 "Open" :
  1449.                 pab->ostatus==HalfOpen ?
  1450.                     "HalfOpen" :
  1451.                     pab->ostatus==NoDisplay ?
  1452.                         "NoDisplay" :
  1453.                     pab->ostatus==Closed ?
  1454.                         "Closed" :
  1455.                         "TotallyClosed",
  1456.         want_status==Open ?
  1457.                 "Open" :
  1458.                 want_status==HalfOpen ?
  1459.                     "HalfOpen" :
  1460.                     want_status==NoDisplay ?
  1461.                         "NoDisplay" :
  1462.                         want_status==Closed ?
  1463.                         "Closed" :
  1464.                         "TotallyClosed"));
  1465.  
  1466.     new_status = want_status;  /* optimistic default */
  1467.  
  1468.     if(want_status == TotallyClosed && pab->address_book != NULL){
  1469.     adrbk_close(pab->address_book);
  1470.     pab->address_book = NULL;
  1471.     }
  1472.     /*
  1473.      * If we don't need it, release some addrbook memory by calling
  1474.      * adrbk_partial_close().
  1475.      */
  1476.     else if((want_status == Closed || want_status == HalfOpen) &&
  1477.     pab->address_book != NULL){
  1478.     adrbk_partial_close(pab->address_book);
  1479.     }
  1480.     /* If we want the addrbook read in and it hasn't been, do so */
  1481.     else if((want_status == Open || want_status == NoDisplay) &&
  1482.     pab->address_book == NULL){
  1483.     if(pab->access != NoAccess){
  1484.         char warning[800]; /* place to put a warning */
  1485.         int sort_rule;
  1486.         int force_not_valid = 0;
  1487.  
  1488.         warning[0] = '\0';
  1489.         if(pab->access == ReadOnly)
  1490.         sort_rule = AB_SORT_RULE_NONE;
  1491.         else
  1492.         sort_rule = ps_global->ab_sort_rule;
  1493.  
  1494.         if(trouble_filename){
  1495.         if(pab->filename)
  1496.           force_not_valid = 
  1497.                    strcmp(trouble_filename, pab->filename) == 0;
  1498.         else
  1499.           force_not_valid = *trouble_filename == '\0';
  1500.  
  1501.         if(force_not_valid){
  1502. #ifdef notdef
  1503.             /*
  1504.              * I go back and forth on whether or not this should
  1505.              * be here.  If the sys admin screws up and copies the
  1506.              * wrong .lu file into place where a large global
  1507.              * addressbook is, do we want it to rebuild for all
  1508.              * the users or not?  With this commented out we're
  1509.              * rebuilding for everybody, just like we would for
  1510.              * a personal addressbook.  Most likely that will mean
  1511.              * that it gets rebuilt in /tmp for each current user.
  1512.              * That's what I'm going with for now.
  1513.              */
  1514.             if(pab->type == LocalGlobal)
  1515.               force_not_valid = 0;
  1516. #endif /* notdef */
  1517.             fs_give((void **)&trouble_filename);
  1518.         }
  1519.         }
  1520.  
  1521.         pab->address_book = adrbk_open(pab->filename,
  1522.            ps_global->home_dir, warning, sort_rule, 0, force_not_valid);
  1523.  
  1524.         if(pab->address_book == NULL){
  1525.         pab->access = NoAccess;
  1526.         if(want_status == Open){
  1527.             new_status = HalfOpen;  /* best we can do */
  1528.             q_status_message1(SM_ORDER | SM_DING, *warning?1:3, 4,
  1529.                       "Error opening/creating address book %s",
  1530.                       pab->nickname);
  1531.             if(*warning)
  1532.             q_status_message2(SM_ORDER, 3, 4, "%s: %s",
  1533.                 as.n_addrbk > 1 ? pab->nickname : "addressbook",
  1534.                 warning);
  1535.         }
  1536.         else
  1537.             new_status  = Closed;
  1538.  
  1539.         dprint(1, (debugfile, "Error opening address book %s: %s\n",
  1540.               pab->nickname, error_description(errno)));
  1541.         }
  1542.         else{
  1543.         if(pab->access == NoExists)
  1544.           pab->access = ReadWrite;
  1545.  
  1546.         /* 200 is out of the blue */
  1547.         (void)adrbk_set_nominal_cachesize(pab->address_book,
  1548.             min((long)adrbk_count(pab->address_book), 200L));
  1549.  
  1550.         if(pab->access == ReadWrite){
  1551. #if !(defined(DOS) && !defined(_WINDOWS))
  1552.             long old_size;
  1553. #endif /* !DOS */
  1554.  
  1555.             /*
  1556.              * Add forced entries if there are any.  These are
  1557.              * entries that are always supposed to show up in
  1558.              * personal address books.  They're specified in the
  1559.              * global config file.
  1560.              */
  1561.             add_forced_entries(pab->address_book);
  1562.  
  1563.             /*
  1564.              * For huge addrbooks, it really pays if you can make
  1565.              * them read-only so that you skip adrbk_is_in_sort_order.
  1566.              */
  1567.             if(!adrbk_is_in_sort_order(pab->address_book, 0)){
  1568. /* DOS sorts will be very slow on large addrbooks */
  1569. #if !(defined(DOS) && !defined(_WINDOWS))
  1570.             old_size =
  1571.                 adrbk_set_nominal_cachesize(pab->address_book,
  1572.                 (long)adrbk_count(pab->address_book));
  1573. #endif /* !DOS */
  1574.             (void)adrbk_sort(pab->address_book,
  1575.                 (a_c_arg_t)0, (adrbk_cntr_t *)NULL, 0);
  1576. #if !(defined(DOS) && !defined(_WINDOWS))
  1577.             (void)adrbk_set_nominal_cachesize(pab->address_book,
  1578.                 old_size);
  1579. #endif /* !DOS */
  1580.             }
  1581.         }
  1582.  
  1583.         new_status = want_status;
  1584.         dprint(1,
  1585.               (debugfile,
  1586.               "Address book %s (%s) opened with %ld items\n",
  1587.                pab->nickname, pab->filename,
  1588.                (long)adrbk_count(pab->address_book)));
  1589.         if(*warning){
  1590.             dprint(1, (debugfile,
  1591.                  "Addressbook parse error in %s (%s): %s\n",
  1592.                  pab->nickname, pab->filename, warning));
  1593.             if(!pab->gave_parse_warnings && want_status == Open){
  1594.             pab->gave_parse_warnings++;
  1595.             q_status_message2(SM_ORDER, 3, 4, "%s: %s",
  1596.                 as.n_addrbk > 1 ? pab->nickname : "addressbook",
  1597.                 warning);
  1598.             }
  1599.         }
  1600.         }
  1601.     }
  1602.     else{
  1603.         if(want_status == Open){
  1604.         new_status = HalfOpen;  /* best we can do */
  1605.         q_status_message1(SM_ORDER | SM_DING, 3, 4,
  1606.            "Insufficient permissions for opening address book %s",
  1607.            pab->nickname);
  1608.         }
  1609.         else
  1610.           new_status = Closed;
  1611.     }
  1612.     }
  1613.  
  1614.     pab->ostatus = new_status;
  1615. }
  1616.  
  1617.  
  1618. /*
  1619.  * Returns the addrbook entry for this display row.
  1620.  */
  1621. AdrBk_Entry *
  1622. ae(row)
  1623.     long row;
  1624. {
  1625.     PerAddrBook *pab;
  1626.     LineType type;
  1627.     AddrScrn_Disp *dl;
  1628.  
  1629.     dl = dlist(row);
  1630.     type = dl->type;
  1631.     if(!(type == Simple || type == ListHead ||
  1632.          type == ListEnt || type == ListClickHere))
  1633.       return((AdrBk_Entry *)NULL);
  1634.  
  1635.     pab = &as.adrbks[adrbk_num_from_lineno(row)];
  1636.  
  1637.     return(adrbk_get_ae(pab->address_book, (a_c_arg_t)dl->elnum, Normal));
  1638. }
  1639.  
  1640.  
  1641. /*
  1642.  * Returns a pointer to the member_number'th list member of the list
  1643.  * associated with this display line.
  1644.  */
  1645. char *
  1646. listmem(row)
  1647.     long row;
  1648. {
  1649.     PerAddrBook *pab;
  1650.     AddrScrn_Disp *dl;
  1651.  
  1652.     dl = dlist(row);
  1653.     if(dl->type != ListEnt)
  1654.       return((char *)NULL);
  1655.  
  1656.     pab = &as.adrbks[adrbk_num_from_lineno(row)];
  1657.  
  1658.     return(listmem_from_dl(pab->address_book, dl));
  1659. }
  1660.  
  1661.  
  1662. /*
  1663.  * Returns a pointer to the list member
  1664.  * associated with this display line.
  1665.  */
  1666. char *
  1667. listmem_from_dl(address_book, dl)
  1668.     AdrBk         *address_book;
  1669.     AddrScrn_Disp *dl;
  1670. {
  1671.     AdrBk_Entry *abe;
  1672.     char **p = (char **)NULL;
  1673.  
  1674.     /* This shouldn't happen */
  1675.     if(dl->type != ListEnt)
  1676.       return((char *)NULL);
  1677.  
  1678.     abe = adrbk_get_ae(address_book, (a_c_arg_t)dl->elnum, Normal);
  1679.  
  1680.     /*
  1681.      * If we wanted to be more careful, We'd go through the list making sure
  1682.      * we don't pass the end.  We'll count on the caller being careful
  1683.      * instead.
  1684.      */
  1685.     if(abe && abe->tag == List){
  1686.     p =  abe->addr.list;
  1687.     p += dl->l_offset;
  1688.     }
  1689.  
  1690.     return((p && *p) ? *p : (char *)NULL);
  1691. }
  1692.  
  1693.  
  1694. DL_CACHE_S *
  1695. dlc_from_listmem(address_book, elem, member_addr)
  1696.     AdrBk       *address_book;
  1697.     a_c_arg_t    elem;
  1698.     char        *member_addr;
  1699. {
  1700.     AdrBk_Entry *abe;
  1701.     char **p;
  1702.     static DL_CACHE_S dlc;
  1703.  
  1704.     abe = adrbk_get_ae(address_book, elem, Normal);
  1705.  
  1706.     if(abe->tag == List){
  1707.     for(p = abe->addr.list; *p; p++){
  1708.         if(strcmp(*p, member_addr) == 0)
  1709.           break;
  1710.     }
  1711.     }
  1712.  
  1713.     dlc.type      = DlcListEnt;
  1714.     dlc.dlcelnum  = (adrbk_cntr_t)elem;
  1715.     if(abe->tag == List)
  1716.       dlc.dlcoffset = p - abe->addr.list;
  1717.     else
  1718.       dlc.dlcoffset = 0;
  1719.  
  1720.     return(&dlc);
  1721. }
  1722.  
  1723.  
  1724. /*
  1725.  * How many members in list?
  1726.  */
  1727. adrbk_cntr_t
  1728. listmem_count_from_abe(abe)
  1729.     AdrBk_Entry *abe;
  1730. {
  1731.     char **p;
  1732.  
  1733.     if(abe->tag != List)
  1734.       return 0;
  1735.  
  1736.     for(p = abe->addr.list; p != NULL && *p != NULL; p++)
  1737.       ;/* do nothing */
  1738.     
  1739.     return((adrbk_cntr_t)(p - abe->addr.list));
  1740. }
  1741.  
  1742.  
  1743. /*
  1744.  * Return a ptr to the row'th line of the global disp_list.
  1745.  * Line numbers count up but you can't count on knowing which line number
  1746.  * goes with the first or the last row.  That is, row 0 is not necessarily
  1747.  * special.  It could be before the rows that make up the display list, after
  1748.  * them, or anywhere in between.  You can't tell what the last row is
  1749.  * numbered, but a dl with type End is returned when you get past the end.
  1750.  * You can't tell what the number of the first row is, but if you go past
  1751.  * the first row a dl of type Beginning will be returned.  Row numbers can
  1752.  * be positive or negative.  Their values have no meaning other than how
  1753.  * they line up relative to other row numbers.
  1754.  */
  1755. AddrScrn_Disp *
  1756. dlist(row)
  1757.     long row;
  1758. {
  1759.     DL_CACHE_S *dlc = (DL_CACHE_S *)NULL;
  1760.  
  1761.     dlc = get_dlc(row);
  1762.  
  1763.     if(dlc){
  1764.     fill_in_dl_field(dlc);
  1765.     return(&dlc->dl);
  1766.     }
  1767.     else{
  1768.     q_status_message(SM_ORDER | SM_DING, 5, 10,
  1769.              "Bug in addrbook, not supposed to happen, re-syncing...");
  1770.     dprint(1,
  1771.         (debugfile,
  1772.     "Bug in addrbook (null dlc in dlist(%ld), not supposed to happen\n",
  1773.         row));
  1774.     /* jump back to a safe starting point */
  1775.     dump_some_debugging("panic_dlist");
  1776.     longjmp(addrbook_changed_unexpectedly, 1);
  1777.     /*NOTREACHED*/
  1778.     }
  1779. }
  1780.  
  1781.  
  1782. /*
  1783.  * This returns the actual dlc instead of the dl within the dlc.
  1784.  */
  1785. DL_CACHE_S *
  1786. get_dlc(row)
  1787.     long row;
  1788. {
  1789.     dprint(11, (debugfile, "- get_dlc(%ld) -\n", row));
  1790.  
  1791.     return(dlc_mgr(row, Lookup, (DL_CACHE_S *)NULL));
  1792. }
  1793.  
  1794.  
  1795. void
  1796. initialize_dlc_cache()
  1797. {
  1798.     dprint(11, (debugfile, "- initialize_dlc_cache -\n"));
  1799.  
  1800.     (void)dlc_mgr(NO_LINE, Initialize, (DL_CACHE_S *)NULL);
  1801. }
  1802.  
  1803.  
  1804. void
  1805. done_with_dlc_cache()
  1806. {
  1807.     dprint(9, (debugfile, "- done_with_dlc_cache -\n"));
  1808.  
  1809.     (void)dlc_mgr(NO_LINE, DoneWithCache, (DL_CACHE_S *)NULL);
  1810. }
  1811.  
  1812.  
  1813. /*
  1814.  * Move to new_dlc and give it row number row_number_to_assign_it.
  1815.  * We copy the passed in dlc in case the caller passed us a pointer into
  1816.  * the cache.
  1817.  */
  1818. void
  1819. warp_to_dlc(new_dlc, row_number_to_assign_it)
  1820.     DL_CACHE_S *new_dlc;
  1821.     long row_number_to_assign_it;
  1822. {
  1823.     DL_CACHE_S dlc;
  1824.  
  1825.     dprint(9, (debugfile, "- warp_to_dlc(%ld) -\n", row_number_to_assign_it));
  1826.  
  1827.     dlc = *new_dlc;
  1828.  
  1829.     (void)dlc_mgr(row_number_to_assign_it, ArbitraryStartingPoint, &dlc);
  1830. }
  1831.  
  1832.  
  1833. /*
  1834.  * Move to first dlc and give it row number 0.
  1835.  */
  1836. void
  1837. warp_to_beginning()
  1838. {
  1839.     dprint(9, (debugfile, "- warp_to_beginning -\n"));
  1840.  
  1841.     (void)dlc_mgr(0L, FirstEntry, (DL_CACHE_S *)NULL);
  1842. }
  1843.  
  1844.  
  1845. /*
  1846.  * Move to last dlc and give it row number 0.
  1847.  */
  1848. void
  1849. warp_to_end()
  1850. {
  1851.     dprint(9, (debugfile, "- warp_to_end -\n"));
  1852.  
  1853.     (void)dlc_mgr(0L, LastEntry, (DL_CACHE_S *)NULL);
  1854. }
  1855.  
  1856.  
  1857. /*
  1858.  * This flushes all of the cache that is related to this address book
  1859.  * entry, (the entry that this cache element refers to).  Or, if this doesn't
  1860.  * refer to an address book entry, it flushes this single cache line.
  1861.  */
  1862. void
  1863. flush_dlc_from_cache(dlc_to_flush)
  1864.     DL_CACHE_S *dlc_to_flush;
  1865. {
  1866.     dprint(11, (debugfile, "- flush_dlc_from_cache -\n"));
  1867.  
  1868.     (void)dlc_mgr(NO_LINE, FlushDlcFromCache, dlc_to_flush);
  1869. }
  1870.  
  1871.  
  1872. /*
  1873.  * Returns 1 if the dlc's match, 0 otherwise.
  1874.  */
  1875. int
  1876. matching_dlcs(dlc1, dlc2)
  1877.     DL_CACHE_S *dlc1, *dlc2;
  1878. {
  1879.     if(!dlc1 || !dlc2 ||
  1880.         dlc1->type != dlc2->type ||
  1881.     dlc1->adrbk_num != dlc2->adrbk_num)
  1882.     return 0;
  1883.  
  1884.     switch(dlc1->type){
  1885.  
  1886.       case DlcSimple:
  1887.       case DlcListHead:
  1888.       case DlcListBlankTop:
  1889.       case DlcListBlankBottom:
  1890.       case DlcListClickHere:
  1891.       case DlcListEmpty:
  1892.     return(dlc1->dlcelnum == dlc2->dlcelnum);
  1893.  
  1894.       case DlcListEnt:
  1895.     return(dlc1->dlcelnum  == dlc2->dlcelnum &&
  1896.            dlc1->dlcoffset == dlc2->dlcoffset);
  1897.  
  1898.       case DlcTitleBlankTop:
  1899.       case DlcTitleDashTop:
  1900.       case DlcTitle:
  1901.       case DlcTitleDashBottom:
  1902.       case DlcTitleBlankBottom:
  1903.       case DlcClickHere:
  1904.       case DlcEmpty:
  1905.       case DlcNoPermission:
  1906.     return 1;
  1907.  
  1908.       case DlcNotSet:
  1909.       case DlcBeginning:
  1910.       case DlcOneBeforeBeginning:
  1911.       case DlcTwoBeforeBeginning:
  1912.       case DlcEnd:
  1913.     return 0;
  1914.     }
  1915.     /*NOTREACHED*/
  1916. }
  1917.  
  1918.  
  1919. /*
  1920.  * Compare two dlc's to see which comes later in the display list.
  1921.  *
  1922.  * THERE ARE BIG RESTRICTIONS!  First, dlc1 must be either a DlcSimple or a
  1923.  * DlcListHead!  Second, these aren't just any dlc's.  Dlc1 is a dlc
  1924.  * that is going to be inserted into the addrbook.  Therefore, it can have
  1925.  * the same element number as something already in the addrbook.  If it
  1926.  * has the same elnum as dlc2, then dlc2 will soon have an elnum that is
  1927.  * one higher when the cache is flushed.  So in that case, dlc1 is earlier
  1928.  * in the display list than dlc2.  But, if dlc1 has an elnum that is one higher
  1929.  * than dlc2's you don't want to decrease it because it is already correct.
  1930.  * In other words, you can't just subtract one from the elnum and then
  1931.  * do a regular compare.  Hence, this weirdo function.
  1932.  *
  1933.  * Actually, it still isn't quite right because it could be that it really
  1934.  * does have the same elnum.  For example, dlc2 could be a ListEnt that
  1935.  * is a member of dlc1, a ListHead.  We'll pretend that can't happen and
  1936.  * note that the consequences of it happening are not serious (display is
  1937.  * still correct).
  1938.  *
  1939.  * Returns > 0 if dlc1 >  dlc2 (comes later in display list),
  1940.  * Returns < 0 if dlc1 <  dlc2 (comes earlier in display list),
  1941.  * (We never call this with dlc1 == dlc2.)
  1942.  */
  1943. int
  1944. funny_compare_dlcs(dlc1, dlc2)
  1945.     DL_CACHE_S *dlc1, *dlc2;
  1946. {
  1947.     long test_elnum;
  1948.  
  1949.     if(!dlc2)
  1950.       return  1; /* arbitrarily, we shouldn't allow this to happen */
  1951.  
  1952.     if(!dlc1)
  1953.       return -1;
  1954.  
  1955.     switch(dlc2->type){
  1956.  
  1957.       case DlcBeginning:
  1958.       case DlcOneBeforeBeginning:
  1959.       case DlcTwoBeforeBeginning:
  1960.     return 1;
  1961.  
  1962.       case DlcEnd:
  1963.     return -1;
  1964.     }
  1965.  
  1966.     if(dlc1->adrbk_num != dlc2->adrbk_num)
  1967.       return(dlc1->adrbk_num - dlc2->adrbk_num);
  1968.  
  1969.     if(dlc1->type != DlcSimple && dlc1->type != DlcListHead)
  1970.       goto panic_abook_abort;
  1971.  
  1972.     switch(dlc2->type){
  1973.  
  1974.       case DlcSimple:
  1975.       case DlcListHead:
  1976.       case DlcListEnt:
  1977.       case DlcListBlankBottom:
  1978.       case DlcListBlankTop:
  1979.       case DlcListClickHere:
  1980.       case DlcListEmpty:
  1981.     /* this is the funny case discussed in the comments */
  1982.     test_elnum = dlc1->dlcelnum;
  1983.     if(test_elnum == dlc2->dlcelnum)
  1984.       test_elnum--;
  1985.  
  1986.     return(test_elnum - dlc2->dlcelnum);
  1987.  
  1988.       case DlcTitleBlankTop:
  1989.       case DlcTitleDashTop:
  1990.       case DlcTitle:
  1991.       case DlcTitleDashBottom:
  1992.       case DlcTitleBlankBottom:
  1993.       case DlcNotSet:
  1994.     return 1;
  1995.  
  1996.       case DlcClickHere:
  1997.       case DlcEmpty:
  1998.       case DlcNoPermission:
  1999.       default:
  2000.     /* panic, not supposed to happen, fall through */
  2001.     break;
  2002.     }
  2003.  
  2004. panic_abook_abort:
  2005.     q_status_message(SM_ORDER | SM_DING, 5, 10,
  2006.              "Bug in addrbook, not supposed to happen, re-syncing...");
  2007.     dprint(1,
  2008.     (debugfile,
  2009.     "Bug in addrbook, not supposed to happen, re-sync\n"));
  2010.     /* jump back to a safe starting point */
  2011.     dump_some_debugging("panic_abook_abort");
  2012.     dump_a_dlc_to_debug("dlc1", dlc1);
  2013.     dump_a_dlc_to_debug("dlc2", dlc2);
  2014.     longjmp(addrbook_changed_unexpectedly, 1);
  2015.     /*NOTREACHED*/
  2016. }
  2017.  
  2018.  
  2019. /*
  2020.  * Returns 1 if the dlc's are related to the same addrbook entry, 0 otherwise.
  2021.  */
  2022. int
  2023. dlcs_from_same_abe(dlc1, dlc2)
  2024.     DL_CACHE_S *dlc1, *dlc2;
  2025. {
  2026.     if(!dlc1 || !dlc2 || dlc1->adrbk_num != dlc2->adrbk_num)
  2027.       return 0;
  2028.  
  2029.     switch(dlc1->type){
  2030.  
  2031.       case DlcSimple:
  2032.       case DlcListHead:
  2033.       case DlcListBlankTop:
  2034.       case DlcListBlankBottom:
  2035.       case DlcListEnt:
  2036.       case DlcListClickHere:
  2037.       case DlcListEmpty:
  2038.     switch(dlc2->type){
  2039.       case DlcTitleBlankTop:
  2040.       case DlcTitleDashTop:
  2041.       case DlcTitle:
  2042.       case DlcTitleDashBottom:
  2043.       case DlcTitleBlankBottom:
  2044.       case DlcClickHere:
  2045.       case DlcEmpty:
  2046.       case DlcNoPermission:
  2047.       case DlcNotSet:
  2048.       case DlcBeginning:
  2049.       case DlcOneBeforeBeginning:
  2050.       case DlcTwoBeforeBeginning:
  2051.       case DlcEnd:
  2052.         return 0;
  2053.  
  2054.       case DlcSimple:
  2055.       case DlcListHead:
  2056.       case DlcListBlankTop:
  2057.       case DlcListBlankBottom:
  2058.       case DlcListEnt:
  2059.       case DlcListClickHere:
  2060.       case DlcListEmpty:
  2061.         return(dlc1->dlcelnum == dlc2->dlcelnum);
  2062.     }
  2063.     break;
  2064.  
  2065.       case DlcTitleBlankTop:
  2066.       case DlcTitleDashTop:
  2067.       case DlcTitle:
  2068.       case DlcTitleDashBottom:
  2069.       case DlcTitleBlankBottom:
  2070.       case DlcClickHere:
  2071.       case DlcEmpty:
  2072.       case DlcNoPermission:
  2073.       case DlcNotSet:
  2074.       case DlcBeginning:
  2075.       case DlcOneBeforeBeginning:
  2076.       case DlcTwoBeforeBeginning:
  2077.       case DlcEnd:
  2078.     return 0;
  2079.     }
  2080.     /*NOTREACHED*/
  2081. }
  2082.  
  2083.  
  2084. /* data for the display list cache */
  2085. static DL_CACHE_S *cache_array = (DL_CACHE_S *)NULL;
  2086. static long        valid_low,
  2087.            valid_high;
  2088. static int         index_of_low,
  2089.            size_of_cache,
  2090.            n_cached;
  2091.  
  2092. /*
  2093.  * Manage the display list cache.
  2094.  *
  2095.  * The cache is a circular array of DL_CACHE_S elements.  It always
  2096.  * contains a contiguous set of display lines.
  2097.  * The lowest numbered line in the cache is
  2098.  * valid_low, and the highest is valid_high.  Everything in between is
  2099.  * also valid.  Index_of_low is where to look
  2100.  * for the valid_low element in the circular array.
  2101.  *
  2102.  * We make calls to dlc_prev and dlc_next to get new entries for the cache.
  2103.  * We need a starting value before we can do that.
  2104.  *
  2105.  * This returns a pointer to a dlc for the desired row.  If you want the
  2106.  * actual display list line you call dlist(row) instead of dlc_mgr.
  2107.  */
  2108. DL_CACHE_S *
  2109. dlc_mgr(row, op, dlc_start)
  2110.     long row;
  2111.     DlMgrOps op;
  2112.     DL_CACHE_S *dlc_start;
  2113. {
  2114.     int                new_index, known_index, next_index;
  2115.     DL_CACHE_S        *dlc = (DL_CACHE_S *)NULL;
  2116.     long               next_row;
  2117.     long               prev_row;
  2118.  
  2119.  
  2120.     if(op == Lookup){
  2121.  
  2122.     if(row >= valid_low && row <= valid_high){  /* already cached */
  2123.  
  2124.         new_index = ((row - valid_low) + index_of_low) % size_of_cache;
  2125.         dlc = &cache_array[new_index];
  2126.  
  2127.     }
  2128.     else if(row > valid_high){  /* row is past where we've looked */
  2129.  
  2130.         known_index =
  2131.           ((valid_high - valid_low) + index_of_low) % size_of_cache;
  2132.         next_row    = valid_high + 1L;
  2133.  
  2134.         /* we'll usually be looking for row = valid_high + 1 */
  2135.         while(next_row <= row){
  2136.  
  2137.         new_index = (known_index + 1) % size_of_cache;
  2138.  
  2139.         dlc =
  2140.           dlc_next(&cache_array[known_index], &cache_array[new_index]);
  2141.  
  2142.         /*
  2143.          * This means somebody changed the file out from underneath
  2144.          * us.  This would happen if dlc_next needed to ask for an
  2145.          * abe to figure out what the type of the next row is, but
  2146.          * adrbk_get_ae returned a NULL.  I don't think that can
  2147.          * happen, but if it does...
  2148.          */
  2149.         if(dlc->type == DlcNotSet){
  2150.             dprint(1, (debugfile, "dlc_next returned DlcNotSet\n"));
  2151.             dump_a_dlc_to_debug("dlc", dlc);
  2152.             goto panic_abook_corrupt;
  2153.         }
  2154.  
  2155.         if(n_cached == size_of_cache){ /* replaced low cache entry */
  2156.             valid_low++;
  2157.             index_of_low = (index_of_low + 1) % size_of_cache;
  2158.         }
  2159.         else
  2160.           n_cached++;
  2161.  
  2162.         valid_high++;
  2163.  
  2164.         next_row++;
  2165.         known_index = new_index; /* for next time through loop */
  2166.         }
  2167.     }
  2168.     else if(row < valid_low){  /* row is back up the screen */
  2169.  
  2170.         known_index = index_of_low;
  2171.         prev_row = valid_low - 1L;
  2172.  
  2173.         while(prev_row >= row){
  2174.  
  2175.         new_index = (known_index - 1 + size_of_cache) % size_of_cache;
  2176.         dlc =
  2177.           dlc_prev(&cache_array[known_index], &cache_array[new_index]);
  2178.  
  2179.         if(dlc->type == DlcNotSet){
  2180.             dprint(1, (debugfile, "dlc_prev returned DlcNotSet (1)\n"));
  2181.             dump_a_dlc_to_debug("dlc", dlc);
  2182.             goto panic_abook_corrupt;
  2183.         }
  2184.  
  2185.         if(n_cached == size_of_cache) /* replaced high cache entry */
  2186.           valid_high--;
  2187.         else
  2188.           n_cached++;
  2189.  
  2190.         valid_low--;
  2191.         index_of_low =
  2192.                 (index_of_low - 1 + size_of_cache) % size_of_cache;
  2193.  
  2194.         prev_row--;
  2195.         known_index = new_index;
  2196.         }
  2197.     }
  2198.     }
  2199.     else if(op == Initialize){
  2200.  
  2201.     n_cached = 0;
  2202.  
  2203.     if(!cache_array || size_of_cache != 3 * as.l_p_page){
  2204.         if(cache_array)
  2205.           free_cache_array(&cache_array, size_of_cache);
  2206.  
  2207.         size_of_cache = 3 * as.l_p_page;
  2208.         cache_array =
  2209.         (DL_CACHE_S *)fs_get(size_of_cache * sizeof(DL_CACHE_S));
  2210.         memset((void *)cache_array, 0, size_of_cache * sizeof(DL_CACHE_S));
  2211.     }
  2212.  
  2213.     /* this will return NULL below and the caller should ignore that */
  2214.     }
  2215.     /*
  2216.      * Flush all rows for a particular addrbook entry from the cache, but
  2217.      * keep the cache alive and anchored in the same place.  The particular
  2218.      * entry is the one that dlc_start is one of the rows of.
  2219.      */
  2220.     else if(op == FlushDlcFromCache){
  2221.     long low_entry;
  2222.  
  2223.     if(dlc_start->type == DlcSimple ||
  2224.        dlc_start->type == DlcListHead ||
  2225.        dlc_start->type == DlcListEnt ||
  2226.        dlc_start->type == DlcListEmpty ||
  2227.        dlc_start->type == DlcListClickHere){
  2228.         /* find this entry in cache */
  2229.         next_row = dlc_start->global_row - 1;
  2230.         for(; next_row >= valid_low; next_row--){
  2231.         next_index = ((next_row - valid_low) + index_of_low) %
  2232.             size_of_cache;
  2233.         if(!dlcs_from_same_abe(dlc_start, &cache_array[next_index]))
  2234.             break;
  2235.         }
  2236.  
  2237.         low_entry = next_row + 1L;
  2238.     }
  2239.     else
  2240.       low_entry = dlc_start->global_row;
  2241.  
  2242.     /*
  2243.      * If low_entry now points one past a ListBlankBottom, delete that,
  2244.      * too, since it may not make sense anymore.
  2245.      */
  2246.     if(low_entry > valid_low){
  2247.         next_index = ((low_entry -1L - valid_low) + index_of_low) %
  2248.         size_of_cache;
  2249.         if(cache_array[next_index].type == DlcListBlankBottom)
  2250.         low_entry--;
  2251.     }
  2252.  
  2253.     if(low_entry > valid_low){ /* invalidate everything >= this */
  2254.         n_cached -= (valid_high - (low_entry - 1L));
  2255.         valid_high = low_entry - 1L;
  2256.     }
  2257.     else{
  2258.         /*
  2259.          * This is the tough case.  That entry was the first thing cached,
  2260.          * so we need to invalidate the whole cache.  However, we also
  2261.          * need to keep at least one thing cached for an anchor, so
  2262.          * we need to get the dlc before this one and it should be a
  2263.          * dlc not related to this same addrbook entry.
  2264.          */
  2265.         known_index = index_of_low;
  2266.         prev_row = valid_low - 1L;
  2267.  
  2268.         for(;;){
  2269.  
  2270.         new_index = (known_index - 1 + size_of_cache) % size_of_cache;
  2271.         dlc =
  2272.           dlc_prev(&cache_array[known_index], &cache_array[new_index]);
  2273.  
  2274.         if(dlc->type == DlcNotSet){
  2275.             dprint(1, (debugfile, "dlc_prev returned DlcNotSet (2)\n"));
  2276.             dump_a_dlc_to_debug("dlc", dlc);
  2277.             goto panic_abook_corrupt;
  2278.         }
  2279.  
  2280.         valid_low--;
  2281.         index_of_low =
  2282.                 (index_of_low - 1 + size_of_cache) % size_of_cache;
  2283.  
  2284.         if(!dlcs_from_same_abe(dlc_start, dlc))
  2285.           break;
  2286.  
  2287.         known_index = new_index;
  2288.         }
  2289.  
  2290.         n_cached = 1;
  2291.         valid_high = valid_low;
  2292.     }
  2293.     }
  2294.     /*
  2295.      * We have to anchor ourselves at a first element.
  2296.      * Here's how we start at the top.
  2297.      */
  2298.     else if(op == FirstEntry){
  2299.     initialize_dlc_cache();
  2300.     n_cached++;
  2301.     dlc = &cache_array[0];
  2302.     dlc = get_global_top_dlc(dlc);
  2303.     dlc->global_row = row;
  2304.     index_of_low = 0;
  2305.     valid_low    = row;
  2306.     valid_high   = row;
  2307.     }
  2308.     /* And here's how we start from the bottom. */
  2309.     else if(op == LastEntry){
  2310.     initialize_dlc_cache();
  2311.     n_cached++;
  2312.     dlc = &cache_array[0];
  2313.     dlc = get_global_bottom_dlc(dlc);
  2314.     dlc->global_row = row;
  2315.     index_of_low = 0;
  2316.     valid_low    = row;
  2317.     valid_high   = row;
  2318.     }
  2319.     /*
  2320.      * And here's how we start from an arbitrary position in the middle.
  2321.      * We root the cache at display line row, so it helps if row is close
  2322.      * to where we're going to be starting so that things are easy to find.
  2323.      * The dl that goes with line row is dl_start from addrbook number
  2324.      * adrbk_num_start.
  2325.      */
  2326.     else if(op == ArbitraryStartingPoint){
  2327.     AddrScrn_Disp      dl;
  2328.  
  2329.     initialize_dlc_cache();
  2330.     n_cached++;
  2331.     dlc = &cache_array[0];
  2332.     /*
  2333.      * Save this in case fill_in_dl_field needs to free the text
  2334.      * it points to.
  2335.      */
  2336.     dl = dlc->dl;
  2337.     *dlc = *dlc_start;
  2338.     dlc->dl = dl;
  2339.     dlc->global_row = row;
  2340.     index_of_low = 0;
  2341.     valid_low    = row;
  2342.     valid_high   = row;
  2343.     }
  2344.     else if(op == DoneWithCache){
  2345.  
  2346.     n_cached = 0;
  2347.     if(cache_array)
  2348.       free_cache_array(&cache_array, size_of_cache);
  2349.     }
  2350.  
  2351.     return(dlc);
  2352.  
  2353. panic_abook_corrupt:
  2354.     q_status_message(SM_ORDER | SM_DING, 5, 10,
  2355.     "Addrbook changed by another process, re-syncing...");
  2356.     dprint(1, (debugfile,
  2357.     "addrbook changed while we had it open?, re-sync\n"));
  2358.     dprint(2, (debugfile,
  2359.     "valid_low=%ld valid_high=%ld index_of_low=%d size_of_cache=%d\n",
  2360.     valid_low, valid_high, index_of_low, size_of_cache));
  2361.     dprint(2, (debugfile,
  2362.     "n_cached=%d new_index=%d known_index=%d next_index=%d\n",
  2363.     n_cached, new_index, known_index, next_index));
  2364.     dprint(2, (debugfile,
  2365.     "next_row=%ld prev_row=%ld row=%ld\n", next_row, prev_row, row));
  2366.     /* jump back to a safe starting point */
  2367.     longjmp(addrbook_changed_unexpectedly, 1);
  2368.     /*NOTREACHED*/
  2369. }
  2370.  
  2371.  
  2372. void
  2373. free_cache_array(c_array, size)
  2374.     DL_CACHE_S **c_array;
  2375.     int size;
  2376. {
  2377.     DL_CACHE_S *dlc;
  2378.     int i;
  2379.  
  2380.     for(i = 0; i < size; i++){
  2381.     dlc = &(*c_array)[i];
  2382.     /* free any allocated space */
  2383.     switch(dlc->dl.type){
  2384.       case Text:
  2385.       case Title:
  2386.         if(dlc->dl.txt)
  2387.           fs_give((void **)&dlc->dl.txt);
  2388.  
  2389.         break;
  2390.     }
  2391.     }
  2392.  
  2393.     fs_give((void **)c_array);
  2394. }
  2395.  
  2396.  
  2397. /*
  2398.  * Get the dlc element that comes before "old".  The function that calls this
  2399.  * function is the one that keeps a cache and checks in the cache before
  2400.  * calling here.  New is a passed in pointer to a buffer where we fill in
  2401.  * the answer.
  2402.  */
  2403. DL_CACHE_S *
  2404. dlc_prev(old, new)
  2405.     DL_CACHE_S *old, *new;
  2406. {
  2407.     PerAddrBook  *pab;
  2408.     AdrBk_Entry  *abe;
  2409.     adrbk_cntr_t  list_count;
  2410.  
  2411.     new->adrbk_num  = -1;
  2412.     new->dlcelnum   = NO_NEXT;
  2413.     new->dlcoffset  = NO_NEXT;
  2414.     new->type       = DlcNotSet;
  2415.     pab = &as.adrbks[old->adrbk_num];
  2416.  
  2417.     switch(old->type){
  2418.       case DlcTitleBlankTop:
  2419.     new->adrbk_num = old->adrbk_num - 1;
  2420.     new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
  2421.     break;
  2422.  
  2423.       case DlcTitleDashTop:
  2424.     if(old->adrbk_num == 0)
  2425.       new->type = DlcOneBeforeBeginning;
  2426.     else
  2427.       new->type = DlcTitleBlankTop;
  2428.  
  2429.     break;
  2430.  
  2431.       case DlcTitle:
  2432.     new->type = DlcTitleDashTop;
  2433.     break;
  2434.  
  2435.       case DlcTitleDashBottom:
  2436.     new->type = DlcTitle;
  2437.     break;
  2438.  
  2439.       case DlcTitleBlankBottom:
  2440.     new->type = DlcTitleDashBottom;
  2441.     break;
  2442.  
  2443.       case DlcClickHere:
  2444.       case DlcEmpty:
  2445.       case DlcNoPermission:
  2446.     if(as.n_addrbk == 1)
  2447.       new->type = DlcOneBeforeBeginning;
  2448.     else
  2449.       new->type = DlcTitleBlankBottom;
  2450.  
  2451.     break;
  2452.  
  2453.       case DlcSimple:
  2454.     if(old->dlcelnum == 0){
  2455.       if(as.n_addrbk == 1)
  2456.         new->type = DlcOneBeforeBeginning;
  2457.       else
  2458.         new->type = DlcTitleBlankBottom;
  2459.     }
  2460.     else{
  2461.         new->dlcelnum = old->dlcelnum - 1;
  2462.         abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum,
  2463.         Normal);
  2464.         if(abe && abe->tag == Single)
  2465.           new->type = DlcSimple;
  2466.         else if(abe && abe->tag == List)
  2467.           new->type = DlcListBlankBottom;
  2468.     }
  2469.  
  2470.     break;
  2471.  
  2472.       case DlcListHead:
  2473.     if(old->dlcelnum == 0){
  2474.         if(as.n_addrbk == 1)
  2475.           new->type = DlcOneBeforeBeginning;
  2476.         else
  2477.           new->type = DlcTitleBlankBottom;
  2478.     }
  2479.     else{
  2480.         new->dlcelnum = old->dlcelnum - 1;
  2481.         abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum,
  2482.         Normal);
  2483.         if(abe && abe->tag == Single){
  2484.         new->type  = DlcListBlankTop;
  2485.         new->dlcelnum = old->dlcelnum;
  2486.         }
  2487.         else if(abe && abe->tag == List)
  2488.           new->type  = DlcListBlankBottom;
  2489.     }
  2490.  
  2491.     break;
  2492.  
  2493.       case DlcListEnt:
  2494.     if(old->dlcoffset > 0){
  2495.         new->type      = DlcListEnt;
  2496.         new->dlcelnum  = old->dlcelnum;
  2497.         new->dlcoffset = old->dlcoffset - 1;
  2498.     }
  2499.     else{
  2500.         new->type     = DlcListHead;
  2501.         new->dlcelnum = old->dlcelnum;
  2502.     }
  2503.  
  2504.     break;
  2505.  
  2506.       case DlcListClickHere:
  2507.       case DlcListEmpty:
  2508.     new->type     = DlcListHead;
  2509.     new->dlcelnum = old->dlcelnum;
  2510.     break;
  2511.  
  2512.       case DlcListBlankTop:  /* can only occur between a Simple and a List */
  2513.     new->type   = DlcSimple;
  2514.     new->dlcelnum  = old->dlcelnum - 1;
  2515.     break;
  2516.  
  2517.       case DlcListBlankBottom:
  2518.     new->dlcelnum = old->dlcelnum;
  2519.     abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum, Normal);
  2520.     if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
  2521.       || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
  2522.         list_count = listmem_count_from_abe(abe);
  2523.         if(list_count == 0)
  2524.           new->type = DlcListEmpty;
  2525.         else{
  2526.         new->type      = DlcListEnt;
  2527.         new->dlcoffset = list_count - 1;
  2528.         }
  2529.     }
  2530.     else
  2531.       new->type = DlcListClickHere;
  2532.  
  2533.     break;
  2534.  
  2535.       case DlcBeginning:
  2536.     new->type   = DlcBeginning;
  2537.     break;
  2538.  
  2539.       case DlcOneBeforeBeginning:
  2540.     new->type   = DlcTwoBeforeBeginning;
  2541.     break;
  2542.  
  2543.       case DlcTwoBeforeBeginning:
  2544.     new->type   = DlcBeginning;
  2545.     break;
  2546.  
  2547.       default:
  2548.     q_status_message(SM_ORDER | SM_DING, 5, 10,
  2549.         "Bug in addrbook, not supposed to happen, re-syncing...");
  2550.     dprint(1,
  2551.         (debugfile,
  2552.         "Bug in addrbook, impossible case (%d) in dlc_prev, re-sync\n",
  2553.         old->type));
  2554.     dump_a_dlc_to_debug("old", old);
  2555.     /* jump back to a safe starting point */
  2556.     longjmp(addrbook_changed_unexpectedly, 1);
  2557.     /*NOTREACHED*/
  2558.     }
  2559.  
  2560.     new->global_row = old->global_row - 1L;
  2561.     if(new->adrbk_num == -1)
  2562.       new->adrbk_num = old->adrbk_num;
  2563.  
  2564.     return(new);
  2565. }
  2566.  
  2567.  
  2568. /*
  2569.  * Get the dlc element that comes after "old".  The function that calls this
  2570.  * function is the one that keeps a cache and checks in the cache before
  2571.  * calling here.
  2572.  */
  2573. DL_CACHE_S *
  2574. dlc_next(old, new)
  2575.     DL_CACHE_S *old, *new;
  2576. {
  2577.     PerAddrBook  *pab;
  2578.     AdrBk_Entry  *abe;
  2579.     adrbk_cntr_t  ab_count;
  2580.     adrbk_cntr_t  list_count;
  2581.  
  2582.     new->adrbk_num  = -1;
  2583.     new->dlcelnum   = NO_NEXT;
  2584.     new->dlcoffset  = NO_NEXT;
  2585.     new->type       = DlcNotSet;
  2586.     pab = &as.adrbks[old->adrbk_num];
  2587.  
  2588.     switch(old->type){
  2589.       case DlcTitleBlankTop:
  2590.     new->type = DlcTitleDashTop;
  2591.     break;
  2592.  
  2593.       case DlcTitleDashTop:
  2594.     new->type = DlcTitle;
  2595.     break;
  2596.  
  2597.       case DlcTitle:
  2598.     new->type = DlcTitleDashBottom;
  2599.     break;
  2600.  
  2601.       case DlcTitleDashBottom:
  2602.     new->type = DlcTitleBlankBottom;
  2603.     break;
  2604.  
  2605.       case DlcTitleBlankBottom:
  2606.     if(pab->ostatus != Open && pab->access != NoAccess)
  2607.       new->type = DlcClickHere;
  2608.     else
  2609.       new = get_first_dl_of_adrbk(old->adrbk_num, new);
  2610.  
  2611.     break;
  2612.  
  2613.       case DlcClickHere:
  2614.       case DlcEmpty:
  2615.       case DlcNoPermission:
  2616.     if(old->adrbk_num == as.n_addrbk - 1)  /* last addrbook */
  2617.       new->type = DlcEnd;
  2618.     else{
  2619.         new->adrbk_num = old->adrbk_num + 1;
  2620.         new->type = DlcTitleBlankTop;
  2621.     }
  2622.  
  2623.     break;
  2624.  
  2625.       case DlcSimple:
  2626.     ab_count = adrbk_count(pab->address_book);
  2627.     if(old->dlcelnum == ab_count - 1){  /* last element of this addrbook */
  2628.         if(old->adrbk_num == as.n_addrbk - 1)  /* last addrbook */
  2629.           new->type = DlcEnd;
  2630.         else{
  2631.         new->adrbk_num = old->adrbk_num + 1;
  2632.         new->type = DlcTitleBlankTop;
  2633.         }
  2634.     }
  2635.     else{
  2636.         new->dlcelnum = old->dlcelnum + 1;
  2637.         abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum,
  2638.         Normal);
  2639.         if(abe->tag == Single)
  2640.           new->type = DlcSimple;
  2641.         else if(abe->tag == List)
  2642.           new->type = DlcListBlankTop;
  2643.     }
  2644.  
  2645.     break;
  2646.  
  2647.       case DlcListHead:
  2648.     new->dlcelnum = old->dlcelnum;
  2649.     abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum, Normal);
  2650.     if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
  2651.       || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
  2652.         list_count = listmem_count_from_abe(abe);
  2653.         if(list_count == 0)
  2654.           new->type = DlcListEmpty;
  2655.         else{
  2656.         new->type      = DlcListEnt;
  2657.         new->dlcoffset = 0;
  2658.         }
  2659.     }
  2660.     else
  2661.       new->type = DlcListClickHere;
  2662.  
  2663.     break;
  2664.  
  2665.       case DlcListEnt:
  2666.     new->dlcelnum = old->dlcelnum;
  2667.     abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum, Normal);
  2668.     list_count = listmem_count_from_abe(abe);
  2669.     if(old->dlcoffset == list_count - 1){  /* last member of list */
  2670.         ab_count = adrbk_count(pab->address_book);
  2671.         if(old->dlcelnum == ab_count - 1){  /* last entry in addrbook */
  2672.         if(old->adrbk_num == as.n_addrbk - 1)  /* last addrbook */
  2673.           new->type = DlcEnd;
  2674.         else{
  2675.             new->type = DlcTitleBlankTop;
  2676.             new->adrbk_num = old->adrbk_num + 1;
  2677.         }
  2678.         }
  2679.         else
  2680.           new->type = DlcListBlankBottom;
  2681.     }
  2682.     else{
  2683.         new->type      = DlcListEnt;
  2684.         new->dlcoffset = old->dlcoffset + 1;
  2685.     }
  2686.  
  2687.     break;
  2688.  
  2689.       case DlcListClickHere:
  2690.       case DlcListEmpty:
  2691.     new->dlcelnum = old->dlcelnum;
  2692.     abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum, Normal);
  2693.     ab_count = adrbk_count(pab->address_book);
  2694.     if(old->dlcelnum == ab_count - 1){  /* last entry in addrbook */
  2695.         if(old->adrbk_num == as.n_addrbk - 1)  /* last addrbook */
  2696.           new->type = DlcEnd;
  2697.         else{
  2698.         new->type = DlcTitleBlankTop;
  2699.         new->adrbk_num = old->adrbk_num + 1;
  2700.         }
  2701.     }
  2702.     else
  2703.       new->type = DlcListBlankBottom;
  2704.  
  2705.     break;
  2706.  
  2707.       case DlcListBlankTop:
  2708.     new->type   = DlcListHead;
  2709.     new->dlcelnum  = old->dlcelnum;
  2710.     break;
  2711.  
  2712.       case DlcListBlankBottom:
  2713.     new->dlcelnum = old->dlcelnum + 1;
  2714.     abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum, Normal);
  2715.     if(abe->tag == Single)
  2716.       new->type = DlcSimple;
  2717.     else if(abe->tag == List)
  2718.       new->type = DlcListHead;
  2719.  
  2720.     break;
  2721.  
  2722.       case DlcEnd:
  2723.     new->type = DlcEnd;
  2724.     break;
  2725.  
  2726.       case DlcOneBeforeBeginning:
  2727.     new = get_global_top_dlc(new);
  2728.     break;
  2729.  
  2730.       case DlcTwoBeforeBeginning:
  2731.     new->type = DlcOneBeforeBeginning;
  2732.     break;
  2733.  
  2734.       default:
  2735.     q_status_message(SM_ORDER | SM_DING, 5, 10,
  2736.         "Bug in addrbook, not supposed to happen, re-syncing...");
  2737.     dprint(1,
  2738.         (debugfile,
  2739.         "Bug in addrbook, impossible case (%d) in dlc_next, re-sync\n",
  2740.         old->type));
  2741.     dump_a_dlc_to_debug("old", old);
  2742.     /* jump back to a safe starting point */
  2743.     longjmp(addrbook_changed_unexpectedly, 1);
  2744.     /*NOTREACHED*/
  2745.     }
  2746.  
  2747.     new->global_row = old->global_row + 1L;
  2748.     if(new->adrbk_num == -1)
  2749.       new->adrbk_num = old->adrbk_num;
  2750.  
  2751.     return(new);
  2752. }
  2753.  
  2754.  
  2755. /*
  2756.  * Get the display line at the very top of whole addrbook screen display.
  2757.  *
  2758.  * If only one addrbook, no titles.
  2759.  */
  2760. DL_CACHE_S *
  2761. get_global_top_dlc(new)
  2762.     DL_CACHE_S *new;  /* fill in answer here */
  2763. {
  2764.     new->dlcelnum   = NO_NEXT;
  2765.     new->dlcoffset  = NO_NEXT;
  2766.     new->type       = DlcNotSet;
  2767.  
  2768.     if(as.n_addrbk > 1)
  2769.       new->type = DlcTitleDashTop;
  2770.     else /* only one addrbook, get top entry */
  2771.       new = get_first_dl_of_adrbk(0, new);
  2772.  
  2773.     new->adrbk_num  = 0;
  2774.  
  2775.     return(new);
  2776. }
  2777.  
  2778.  
  2779. /*
  2780.  * Get the last display line for the whole address book screen.
  2781.  * This gives us a way to start at the end and move back up.
  2782.  */
  2783. DL_CACHE_S *
  2784. get_global_bottom_dlc(new)
  2785.     DL_CACHE_S *new;  /* fill in answer here */
  2786. {
  2787.     new->dlcelnum   = NO_NEXT;
  2788.     new->dlcoffset  = NO_NEXT;
  2789.     new->type       = DlcNotSet;
  2790.  
  2791.     new->adrbk_num = as.n_addrbk - 1;
  2792.  
  2793.     new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
  2794.  
  2795.     return(new);
  2796. }
  2797.  
  2798.  
  2799. /*
  2800.  * First dl in a particular addrbook, not counting title lines.
  2801.  */
  2802. DL_CACHE_S *
  2803. get_first_dl_of_adrbk(adrbk_num, new)
  2804.     int         adrbk_num;
  2805.     DL_CACHE_S *new;  /* fill in answer here */
  2806. {
  2807.     PerAddrBook  *pab;
  2808.     AdrBk_Entry  *abe;
  2809.     adrbk_cntr_t  ab_count;
  2810.  
  2811.     pab = &as.adrbks[adrbk_num];
  2812.  
  2813.     if(pab->access == NoAccess)
  2814.       new->type = DlcNoPermission;
  2815.     else{
  2816.     ab_count = adrbk_count(pab->address_book);
  2817.     if(ab_count == 0)
  2818.       new->type = DlcEmpty;
  2819.     else{
  2820.         new->dlcelnum = 0;
  2821.         abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum,
  2822.         Normal);
  2823.         if(abe->tag == Single)
  2824.           new->type = DlcSimple;
  2825.         else if(abe->tag == List)
  2826.           new->type = DlcListHead;
  2827.     }
  2828.     }
  2829.  
  2830.     return(new);
  2831. }
  2832.  
  2833.  
  2834. /*
  2835.  * Find the last display line for addrbook number adrbk_num.
  2836.  */
  2837. DL_CACHE_S *
  2838. get_bottom_dl_of_adrbk(adrbk_num, new)
  2839.     int         adrbk_num;
  2840.     DL_CACHE_S *new;  /* fill in answer here */
  2841. {
  2842.     PerAddrBook  *pab;
  2843.     AdrBk_Entry  *abe;
  2844.     adrbk_cntr_t  ab_count;
  2845.     adrbk_cntr_t  list_count;
  2846.  
  2847.     pab = &as.adrbks[adrbk_num];
  2848.  
  2849.     if(pab->ostatus != Open){
  2850.     if(pab->access == NoAccess)
  2851.       new->type = DlcNoPermission;
  2852.     else
  2853.       new->type = DlcClickHere;
  2854.     }
  2855.     else{
  2856.     ab_count = adrbk_count(pab->address_book);
  2857.     if(ab_count == 0)
  2858.       new->type = DlcEmpty;
  2859.     else{
  2860.         new->dlcelnum = ab_count - 1;
  2861.         abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum,
  2862.         Normal);
  2863.         if(abe->tag == Single)
  2864.           new->type = DlcSimple;
  2865.         else if(abe->tag == List){
  2866.         if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
  2867.            || exp_is_expanded(pab->address_book->exp,
  2868.                       (a_c_arg_t)new->dlcelnum)){
  2869.             list_count = listmem_count_from_abe(abe);
  2870.             if(list_count == 0)
  2871.               new->type = DlcListEmpty;
  2872.             else{
  2873.             new->type      = DlcListEnt;
  2874.             new->dlcoffset = list_count - 1;
  2875.             }
  2876.         }
  2877.         else
  2878.           new->type = DlcListClickHere;
  2879.         }
  2880.     }
  2881.     }
  2882.  
  2883.     return(new);
  2884. }
  2885.  
  2886.  
  2887. /*
  2888.  * Uses information in new to fill in new->dl.
  2889.  */
  2890. void
  2891. fill_in_dl_field(new)
  2892.     DL_CACHE_S *new;
  2893. {
  2894.     AddrScrn_Disp *dl;
  2895.     PerAddrBook   *pab;
  2896.     char buf[MAX_SCREEN_COLS + 1];
  2897.     char buf2[1024];
  2898.     int screen_width = ps_global->ttyo->screen_cols;
  2899.     int len;
  2900.  
  2901.     buf[MAX_SCREEN_COLS] = '\0';
  2902.     screen_width = min(MAX_SCREEN_COLS, screen_width);
  2903.  
  2904.     dl = &(new->dl);
  2905.  
  2906.     /* free any previously allocated space */
  2907.     switch(dl->type){
  2908.       case Text:
  2909.       case Title:
  2910.     if(dl->txt)
  2911.       fs_give((void **)&dl->txt);
  2912.     }
  2913.  
  2914.     /* set up new dl */
  2915.     switch(new->type){
  2916.       case DlcTitleBlankTop:
  2917.       case DlcTitleBlankBottom:
  2918.       case DlcListBlankTop:
  2919.       case DlcListBlankBottom:
  2920.     dl->type = Text;
  2921.     dl->txt  = cpystr("");
  2922.     break;
  2923.  
  2924.       case DlcTitleDashTop:
  2925.       case DlcTitleDashBottom:
  2926.     /* line of dashes in txt field */
  2927.     dl->type = Text;
  2928.     memset((void *)buf, '-', screen_width * sizeof(char));
  2929.     buf[screen_width] = '\0';
  2930.     dl->txt = cpystr(buf);
  2931.     break;
  2932.  
  2933.       case DlcNoPermission:
  2934.     dl->type = Text;
  2935.     dl->txt  = cpystr(NO_PERMISSION);
  2936.     break;
  2937.  
  2938.       case DlcTitle:
  2939.     dl->type = Title;
  2940.     pab = &as.adrbks[new->adrbk_num];
  2941.         /* title for this addrbook */
  2942.         memset((void *)buf, SPACE, screen_width * sizeof(char));
  2943.     buf[screen_width] = '\0';
  2944.         sprintf(buf2, "%s AddressBook <%s>",
  2945.             (new->adrbk_num < as.how_many_personals) ?
  2946.             "Personal" :
  2947.             "Global",
  2948.                     pab->nickname ? pab->nickname : pab->filename);
  2949.         len = strlen(buf2);
  2950.         strncpy(buf, buf2, len);
  2951.         if(as.ro_warning){
  2952.             char *q;
  2953.  
  2954.             if(pab->access == ReadOnly){
  2955.                 if(screen_width - len - (int)strlen(READONLY) > 3){
  2956.                     q = buf + screen_width - strlen(READONLY);
  2957.                     strcpy(q, READONLY);
  2958.                 }
  2959.             }
  2960.             else if(pab->access == NoAccess){
  2961.                 if(screen_width - len - (int)strlen(NOACCESS) > 3){
  2962.                     q = buf + screen_width - strlen(NOACCESS);
  2963.                     strcpy(q, NOACCESS);
  2964.                 }
  2965.             }
  2966.         }
  2967.  
  2968.     dl->txt = cpystr(buf);
  2969.     break;
  2970.  
  2971.       case DlcClickHere:
  2972.     dl->type = ClickHere;
  2973.     break;
  2974.  
  2975.       case DlcListClickHere:
  2976.     dl->type  = ListClickHere;
  2977.     dl->elnum = new->dlcelnum;
  2978.     break;
  2979.  
  2980.       case DlcListEmpty:
  2981.     dl->type  = ListEmpty;
  2982.     dl->elnum = new->dlcelnum;
  2983.     break;
  2984.  
  2985.       case DlcEmpty:
  2986.     dl->type = Empty;
  2987.     break;
  2988.  
  2989.       case DlcSimple:
  2990.     dl->type  = Simple;
  2991.     dl->elnum = new->dlcelnum;
  2992.     break;
  2993.  
  2994.       case DlcListHead:
  2995.     dl->type  = ListHead;
  2996.     dl->elnum = new->dlcelnum;
  2997.     break;
  2998.  
  2999.       case DlcListEnt:
  3000.     dl->type     = ListEnt;
  3001.     dl->elnum    = new->dlcelnum;
  3002.     dl->l_offset = new->dlcoffset;
  3003.     break;
  3004.  
  3005.       case DlcBeginning:
  3006.       case DlcOneBeforeBeginning:
  3007.       case DlcTwoBeforeBeginning:
  3008.     dl->type = Beginning;
  3009.     break;
  3010.  
  3011.       case DlcEnd:
  3012.     dl->type = End;
  3013.     break;
  3014.  
  3015.       default:
  3016.     q_status_message(SM_ORDER | SM_DING, 5, 10,
  3017.         "Bug in addrbook, not supposed to happen, re-syncing...");
  3018.     dprint(1,
  3019.         (debugfile,
  3020.         "Bug in addrbook, impossible dflt in fill_in_dl (%d)\n",
  3021.         new->type));
  3022.     dump_a_dlc_to_debug("new", new);
  3023.     /* jump back to a safe starting point */
  3024.     longjmp(addrbook_changed_unexpectedly, 1);
  3025.     /*NOTREACHED*/
  3026.     }
  3027. }
  3028.  
  3029.  
  3030. /*
  3031.  * Args: start_disp     --  line to start displaying on when redrawing, 0 is
  3032.  *                 the top_of_screen
  3033.  *       cur_line       --  current line number (0 is 1st line we display)
  3034.  *       old_line       --  old line number
  3035.  *       redraw         --  flag requesting redraw as opposed to update of
  3036.  *                current line
  3037.  *     start_pos        --  return position where highlighted text begins here
  3038.  *
  3039.  * Result: lines painted on the screen
  3040.  *
  3041.  * It either redraws the screen from line "start_disp" down or
  3042.  * moves the cursor from one field to another.
  3043.  */
  3044. void
  3045. display_book(start_disp, cur_line, old_line, redraw, start_pos)
  3046.     int  start_disp,
  3047.      cur_line,
  3048.      old_line,
  3049.      redraw;
  3050.     Pos *start_pos;
  3051. {
  3052.     int screen_row, highlight;
  3053.     long global_row;
  3054.     Pos sp;
  3055.  
  3056.     dprint(9, (debugfile,
  3057.     "- display_book() -\n   top %d start %d cur_line %d redraw %d\n",
  3058.     as.top_ent, start_disp, cur_line, redraw));
  3059.  
  3060.     if(start_pos){
  3061.     start_pos->row = 0;
  3062.     start_pos->col = 0;
  3063.     }
  3064.  
  3065.     if(as.l_p_page <= 0)
  3066.       return;
  3067.  
  3068. #ifdef _WINDOWS
  3069.     mswin_beginupdate();
  3070. #endif
  3071.     if(redraw){
  3072.         /*--- Repaint all of the screen or bottom part of screen ---*/
  3073.         global_row = as.top_ent + start_disp;
  3074.         for(screen_row = start_disp;
  3075.         screen_row < as.l_p_page;
  3076.         screen_row++, global_row++){
  3077.  
  3078.             highlight = (screen_row == cur_line);
  3079.         ClearLine(screen_row + HEADER_ROWS(ps_global));
  3080.             paint_line(screen_row + HEADER_ROWS(ps_global), global_row,
  3081.         highlight, &sp);
  3082.         if(start_pos && highlight)
  3083.           *start_pos = sp;
  3084.         }
  3085.     }
  3086.     else{
  3087.  
  3088.         /*--- Only update current, or move the cursor ---*/
  3089.         if(cur_line != old_line){
  3090.  
  3091.             /*--- Repaint old position to erase "cursor" ---*/
  3092.             paint_line(old_line + HEADER_ROWS(ps_global), as.top_ent + old_line,
  3093.                        0, &sp);
  3094.         }
  3095.  
  3096.         /*--- paint the position with the cursor ---*/
  3097.         paint_line(cur_line + HEADER_ROWS(ps_global), as.top_ent + cur_line,
  3098.                    1, &sp);
  3099.     if(start_pos)
  3100.       *start_pos = sp;
  3101.     }
  3102.  
  3103. #ifdef _WINDOWS
  3104.     scroll_setpos(as.top_ent);
  3105.     mswin_endupdate();
  3106. #endif
  3107.     fflush(stdout);
  3108. }
  3109.  
  3110.  
  3111. /*
  3112.  * Paint a line on the screen
  3113.  *
  3114.  * Args: line    --  Line on screen to paint
  3115.  *    global_row --  Row number of line to paint
  3116.  *     highlight --  Line should be highlighted
  3117.  *     start_pos --  return position where text begins here
  3118.  *
  3119.  * Result: Line is painted
  3120.  *
  3121.  * The three field widths for the formatting are passed in.  There is an
  3122.  * implicit 2 spaces between the fields.
  3123.  *
  3124.  *    | fld_width[0] chars |__| fld_width[1] |__| fld_width[2] | ...
  3125.  */
  3126. void
  3127. paint_line(line, global_row, highlight, start_pos)
  3128.     int  line;
  3129.     long global_row;
  3130.     int  highlight;
  3131.     Pos *start_pos;
  3132. {
  3133.     int          fld_col[NFIELDS],
  3134.           fld_width[NFIELDS],
  3135.           screen_width,
  3136.           col,
  3137.           scol = -1,
  3138.           fld;
  3139.     char      fld_control[NFIELDS][11],
  3140.           full_control[11];
  3141.     char      *string;
  3142.     AddrScrn_Disp *dl;
  3143.     AdrBk_Entry      *abe;
  3144.     PerAddrBook      *pab;
  3145.  
  3146.     dprint(10, (debugfile, "- paint_line(%d, %d) -\n", line, highlight));
  3147.  
  3148.     dl  = dlist(global_row);
  3149.     pab = &as.adrbks[adrbk_num_from_lineno(global_row)];
  3150.     start_pos->row = line;
  3151.     start_pos->col = 0; /* default */
  3152.  
  3153.     switch(dl->type){
  3154.       case Beginning:
  3155.       case End:
  3156.         return;
  3157.     }
  3158.  
  3159.     screen_width = ps_global->ttyo->screen_cols;
  3160.     sprintf(full_control, "%%-%d.%ds",   screen_width, screen_width);
  3161.  
  3162.     if(highlight)
  3163.       StartInverse();
  3164.  
  3165.     /* the types in this set span all columns */
  3166.     switch(dl->type){
  3167.       case Text:
  3168.       case Title:
  3169.       case ClickHere:
  3170.       case Empty:
  3171.     if(dl->type == ClickHere)
  3172.       string = CLICKHERE;
  3173.     else if(dl->type == Empty)
  3174.       string = EMPTY;
  3175.     else
  3176.       string = dl->txt;
  3177.  
  3178.     /* center it */
  3179.     col = (screen_width - (int)strlen(string))/2;
  3180.     if(col >= 0)
  3181.       PutLine0(line, col, string);
  3182.     else{
  3183.         col = 0;
  3184.         PutLine1(line, col, full_control, string);
  3185.     }
  3186.  
  3187.     start_pos->col = col;
  3188.     if(highlight)
  3189.       EndInverse();
  3190.  
  3191.     return;
  3192.     }
  3193.  
  3194.     for(fld = 0; fld < NFIELDS; fld++)
  3195.       fld_width[fld] = pab->disp_form[fld].width;
  3196.  
  3197.     fld_col[0] = 0;
  3198.     for(fld = 1; fld < NFIELDS; fld++)
  3199.       fld_col[fld] = min(fld_col[fld-1]+fld_width[fld-1]+2, screen_width-1);
  3200.  
  3201.     for(fld = 0; fld < NFIELDS; fld++){
  3202.         if(pab->disp_form[fld].wtype == Special){
  3203.             sprintf(fld_control[fld], "%%-%d.%ds",
  3204.         fld_width[fld],fld_width[fld]);
  3205.         fld_col[fld] = screen_width - fld_width[fld];
  3206.     }
  3207.         else if((fld+1) == NFIELDS
  3208.           || pab->disp_form[fld+1].type == Notused
  3209.           || pab->disp_form[fld+1].type == WhenNoAddrDisplayed)
  3210.           sprintf(fld_control[fld], "%%-%d.%ds",
  3211.           fld_width[fld],fld_width[fld]);
  3212.         else
  3213.           sprintf(fld_control[fld], "%%-%d.%ds  ",
  3214.           fld_width[fld],fld_width[fld]);
  3215.     }
  3216.       
  3217.     for(fld = 0; fld < NFIELDS; fld++){
  3218.       if(fld_width[fld] == 0)
  3219.     continue;
  3220.  
  3221.       switch(pab->disp_form[fld].type){
  3222.     case Notused:
  3223.       break;
  3224.  
  3225.     case Nickname:
  3226.       switch(dl->type){
  3227.         case Simple:
  3228.         case ListHead:
  3229.           abe = ae(global_row);
  3230.           string = (abe && abe->nickname) ? abe->nickname : "";
  3231.           if(scol == -1)
  3232.         scol = fld_col[fld];
  3233.  
  3234.           PutLine1(line, fld_col[fld], fld_control[fld], string);
  3235.           break;
  3236.       }
  3237.       break;
  3238.  
  3239.     case Fullname:
  3240.       switch(dl->type){
  3241.         case Simple:
  3242.         case ListHead:
  3243.           abe = ae(global_row);
  3244.           string = (abe && abe->fullname) ? abe->fullname : "";
  3245.           if(scol == -1)
  3246.         scol = fld_col[fld];
  3247.  
  3248.           PutLine1(line, fld_col[fld], fld_control[fld],
  3249.           (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3250.                                   string, NULL));
  3251.           break;
  3252.  
  3253.         case ListEnt:
  3254.           /* continuation line */
  3255.           if(line == HEADER_ROWS(ps_global)){
  3256.             char temp[50];
  3257.         int  i, width, width1, width2;
  3258.  
  3259.         /*
  3260.          * This field may overflow into next field because the
  3261.          * fullname field doesn't usually come on the same line
  3262.          * as the next field but does this time because it is
  3263.          * a continuation line.
  3264.          */
  3265.         width = fld_width[fld];
  3266.         for(i = fld+1; i < NFIELDS && fld_width[i] > 0; i++){
  3267.             if(fld_col[fld] + width + 2 > fld_col[i]){
  3268.             width = max(fld_col[i] - fld_col[fld] - 2, 0);
  3269.             break;
  3270.             }
  3271.         }
  3272.  
  3273.         width2 = min(11, width);
  3274.         width1 = max(width - width2 - 1, 0);
  3275.             abe = ae(global_row);
  3276.             string = (abe && abe->fullname) ? abe->fullname : "";
  3277.             sprintf(temp, "%.*s%s%.*s", width1,
  3278.           (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3279.                                   string, NULL),
  3280.           width1 ? " " : "",
  3281.           width2,  "(continued)");
  3282.         if(highlight)
  3283.           EndInverse();
  3284.  
  3285.             PutLine1(line, fld_col[fld], fld_control[fld], temp);
  3286.         if(highlight)
  3287.           StartInverse();
  3288.           }
  3289.           break;
  3290.       }
  3291.       break;
  3292.  
  3293.     case Addr:
  3294.       switch(dl->type){
  3295.         case ListClickHere:
  3296.         case ListEmpty:
  3297.           if(dl->type == ListClickHere)
  3298.         string = CLICKHERE;
  3299.           else
  3300.             string = EMPTY;
  3301.  
  3302.           /* ok to go past edge of field for these types */
  3303.           if((screen_width - fld_col[fld]) >= (int)strlen(string))
  3304.         col = fld_col[fld];  /* left-adjusted in column */
  3305.           else
  3306.         col = screen_width - (int)strlen(string);
  3307.  
  3308.           if(col >= 0)
  3309.             PutLine0(line, col, string);
  3310.           else{
  3311.               col = 0;
  3312.               PutLine1(line, col, full_control, string);
  3313.           }
  3314.  
  3315.           if(scol == -1)
  3316.         scol = col;
  3317.  
  3318.           break;
  3319.  
  3320.         case ListHead:
  3321.           if(scol == -1)
  3322.         scol = fld_col[fld];
  3323.  
  3324.           PutLine1(line, fld_col[fld], fld_control[fld], DISTLIST);
  3325.           break;
  3326.  
  3327.         case Simple:
  3328.           abe = ae(global_row);
  3329.           string = (abe && abe->tag == Single && abe->addr.addr) ?
  3330.           abe->addr.addr : "";
  3331.           if(scol == -1)
  3332.         scol = fld_col[fld];
  3333.  
  3334.           PutLine1(line, fld_col[fld], fld_control[fld], string);
  3335.           break;
  3336.  
  3337.         case ListEnt:
  3338.           string = listmem(global_row) ? listmem(global_row) : "";
  3339.           if(scol == -1)
  3340.         scol = fld_col[fld];
  3341.  
  3342.           PutLine1(line, fld_col[fld], fld_control[fld],
  3343.           (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3344.                               string, NULL));
  3345.           break;
  3346.       }
  3347.       break;
  3348.  
  3349.     case Filecopy:
  3350.     case Comment:
  3351.       switch(dl->type){
  3352.         case Simple:
  3353.         case ListHead:
  3354.           abe = ae(global_row);
  3355.           if(pab->disp_form[fld].type == Filecopy)
  3356.             string = (abe && abe->fcc) ? abe->fcc : "";
  3357.           else
  3358.             string = (abe && abe->extra) ? abe->extra : "";
  3359.  
  3360.           if(scol == -1)
  3361.         scol = fld_col[fld];
  3362.  
  3363.           PutLine1(line, fld_col[fld], fld_control[fld],
  3364.           (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3365.                                   string, NULL));
  3366.           break;
  3367.       }
  3368.       break;
  3369.  
  3370.     case Checkbox:
  3371.       switch(dl->type){
  3372.         case Simple:
  3373.         case ListHead:
  3374.           if(entry_is_checked(pab->address_book->checks,
  3375.                   (a_c_arg_t)dl->elnum))
  3376.         string = "[X]";
  3377.           else
  3378.             string = "[ ]";
  3379.  
  3380.           if(scol == -1)
  3381.         scol = fld_col[fld];
  3382.  
  3383.           PutLine1(line, fld_col[fld], fld_control[fld], string);
  3384.           break;
  3385.       }
  3386.       break;
  3387.  
  3388.     case WhenNoAddrDisplayed:
  3389.       switch(dl->type){
  3390.         case ListClickHere:
  3391.         case ListEmpty:
  3392.         case ListEnt:
  3393.           if(dl->type == ListClickHere)
  3394.         string = CLICKHERE;
  3395.           else if(dl->type == ListEmpty)
  3396.             string = EMPTY;
  3397.           else
  3398.         string = listmem(global_row)
  3399.               ? (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3400.                               listmem(global_row), NULL)
  3401.               : "";
  3402.  
  3403.           if(scol == -1)
  3404.         scol = fld_col[fld];
  3405.  
  3406.           PutLine1(line, fld_col[fld], fld_control[fld], string?string:"");
  3407.           break;
  3408.       }
  3409.       break;
  3410.       }
  3411.     }
  3412.  
  3413.     if(highlight)
  3414.       EndInverse();
  3415.     
  3416.     if(scol > 0)
  3417.       start_pos->col = scol;
  3418. }
  3419.  
  3420.  
  3421.  
  3422. /*
  3423.  * Set field widths for the columns of the display.  The idea is to
  3424.  * try to come up with something that works pretty well.  Getting it just
  3425.  * right isn't important.
  3426.  *
  3427.  * Col1 and col2 are arrays which contain some widths useful for
  3428.  * formatting the screen.  The 0th element is the max
  3429.  * width in that column.  The 1st element is the max of the third largest
  3430.  * width in each addressbook (yup, strange).
  3431.  *
  3432.  * The info above applies to the default case (AllAuto).  The newer methods
  3433.  * where the user specifies the format is the else part of the big if and
  3434.  * is quite a bit different.  It's all sort of ad hoc when we're asked
  3435.  * to calculate one of the fields for the user.
  3436.  *
  3437.  * Returns non-zero if the widths changed since the last time called.
  3438.  */
  3439. int
  3440. calculate_field_widths()
  3441. {
  3442.   int space_left, i, j, screen_width;
  3443.   int ret = 0;
  3444.   PerAddrBook *pab;
  3445.   WIDTH_INFO_S *widths;
  3446.   int max_nick, max_full, max_addr, third_full, third_addr, third_fcc;
  3447.   int col1[5], col2[5];
  3448.   int nick = -1, full = -1, addr = -1;
  3449. #define SEP 2  /* space between columns */
  3450.  
  3451.   dprint(9, (debugfile, "- calculate_field_widths -\n"));
  3452.  
  3453.   screen_width = ps_global->ttyo->screen_cols;
  3454.  
  3455.   /* calculate widths for each addrbook independently */
  3456.   for(j = 0; j < as.n_addrbk; j++){
  3457.     pab = &as.adrbks[j];
  3458.  
  3459.     max_nick   = 0;
  3460.     max_full   = 2;
  3461.     max_addr   = 2;
  3462.     third_full = 2;
  3463.     third_addr = 2;
  3464.     third_fcc  = 2;
  3465.  
  3466.     if(pab->ostatus == Open || pab->ostatus == NoDisplay){
  3467.       widths = &pab->address_book->widths;
  3468.       max_nick   = min(max(max_nick, widths->max_nickname_width), 25);
  3469.       max_full   = max(max_full, widths->max_fullname_width);
  3470.       max_addr   = max(max_addr, widths->max_addrfield_width);
  3471.       third_full = max(third_full, widths->third_biggest_fullname_width);
  3472.       if(third_full == 2)
  3473.         third_full = max(third_full, 2*max_full/3);
  3474.  
  3475.       third_addr = max(third_addr, widths->third_biggest_addrfield_width);
  3476.       if(third_addr == 2)
  3477.         third_addr = max(third_addr, 2*max_addr/3);
  3478.  
  3479.       third_fcc  = max(third_fcc, widths->third_biggest_fccfield_width);
  3480.       if(third_fcc == 2)
  3481.         third_fcc = max(third_fcc, 2*widths->max_fccfield_width/3);
  3482.     }
  3483.  
  3484.     /* figure out which order they're in and reset widths */
  3485.     for(i = 0; i < NFIELDS; i++){
  3486.       pab->disp_form[i].width = 0;
  3487.       switch(pab->disp_form[i].type){
  3488.         case Nickname:
  3489.       nick = i;
  3490.       break;
  3491.  
  3492.     case Fullname:
  3493.       full = i;
  3494.       break;
  3495.  
  3496.     case Addr:
  3497.       addr = i;
  3498.       break;
  3499.       }
  3500.     }
  3501.  
  3502.     /* Compute default format */
  3503.     if(pab->disp_form[1].wtype == AllAuto){
  3504.  
  3505.       col1[0] = max_full;
  3506.       col2[0] = max_addr;
  3507.       col1[1] = third_full;
  3508.       col2[1] = third_addr;
  3509.       col1[2] = 3;
  3510.       col2[2] = 3;
  3511.       col1[3] = 2;
  3512.       col2[3] = 2;
  3513.       col1[4] = 1;
  3514.       col2[4] = 1;
  3515.  
  3516.       space_left = screen_width;
  3517.  
  3518.       if(pab->disp_form[0].type == Checkbox){
  3519.     pab->disp_form[0].width = min(pab->disp_form[0].req_width,space_left);
  3520.     space_left = max(space_left-(pab->disp_form[0].width + SEP), 0);
  3521.       }
  3522.  
  3523.       /*
  3524.        * All of the nickname field should be visible,
  3525.        * and make it at least 3.
  3526.        */
  3527.       pab->disp_form[nick].width = min(max(max_nick, 3), space_left);
  3528.  
  3529.       /*
  3530.        * The SEP is for two blank columns between nickname and next field.
  3531.        * Those blank columns are taken automatically in paint_line().
  3532.        */
  3533.       space_left -= (pab->disp_form[nick].width + SEP);
  3534.  
  3535.       if(space_left > 0){
  3536.     for(i = 0; i < 5; i++){
  3537.       /* try fitting most of each field in if possible */
  3538.       if(col1[i] + SEP + col2[i] <= space_left){
  3539.         int extra;
  3540.  
  3541.         extra = space_left - col1[i] - SEP - col2[i];
  3542.         /*
  3543.          * try to stabilize nickname column shifts
  3544.          * so that screen doesn't jump around when we make changes
  3545.          */
  3546.         if(i == 0 && pab->disp_form[nick].width < 7 &&
  3547.             extra >= (7 - pab->disp_form[nick].width)){
  3548.           extra -= (7 - pab->disp_form[nick].width);
  3549.           space_left -= (7 - pab->disp_form[nick].width);
  3550.           pab->disp_form[nick].width = 7;
  3551.         }
  3552.  
  3553.         pab->disp_form[addr].width = col2[i] + extra/2;
  3554.         pab->disp_form[full].width =
  3555.                 space_left - SEP - pab->disp_form[addr].width;
  3556.         break;
  3557.       }
  3558.     }
  3559.  
  3560.     /*
  3561.      * None of them would fit.  Toss addr field.
  3562.      */
  3563.     if(i == 5){
  3564.       pab->disp_form[full].width = space_left;
  3565.       pab->disp_form[addr].width = 0;
  3566.     }
  3567.       }
  3568.       else{
  3569.     pab->disp_form[full].width = 0;
  3570.     pab->disp_form[addr].width = 0;
  3571.       }
  3572.  
  3573.       dprint(10, (debugfile, "Using %s choice: %d %d %d", enth_string(i+1),
  3574.         pab->disp_form[nick].width, pab->disp_form[full].width,
  3575.         pab->disp_form[addr].width));
  3576.     }
  3577.     else{  /* non-default case */
  3578.       int some_to_calculate = 0;
  3579.       int columns = 0;
  3580.       int used = 0;
  3581.       int avail_screen;
  3582.       int all_percents = 1;
  3583.       int pc_tot;
  3584.  
  3585.       /*
  3586.        * First count how many fields there are.
  3587.        * Fill in all the Fixed's while we're at it.
  3588.        */
  3589.       for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
  3590.     if(pab->disp_form[i].wtype == Fixed){
  3591.       pab->disp_form[i].width = pab->disp_form[i].req_width;
  3592.       all_percents = 0;
  3593.     }
  3594.     else if(pab->disp_form[i].wtype == WeCalculate){
  3595.       pab->disp_form[i].width = pab->disp_form[i].req_width; /* for now */
  3596.       some_to_calculate++;
  3597.       all_percents = 0;
  3598.     }
  3599.  
  3600.     if(pab->disp_form[i].wtype != Special){
  3601.       used += pab->disp_form[i].width;
  3602.       columns++;
  3603.     }
  3604.       }
  3605.  
  3606.       used += ((columns-1) * SEP);
  3607.       avail_screen = screen_width - used;
  3608.  
  3609.       /*
  3610.        * Now that we know how much space we've got, we can
  3611.        * calculate the Percent columns.
  3612.        */
  3613.       if(avail_screen > 0){
  3614.         for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
  3615.       if(pab->disp_form[i].wtype == Percent){
  3616.         /* The 2, 200, and +100 are because we're rounding */
  3617.         pab->disp_form[i].width =
  3618.              ((2*pab->disp_form[i].req_width*avail_screen)+100) / 200;
  3619.         used += pab->disp_form[i].width;
  3620.       }
  3621.         }
  3622.       }
  3623.  
  3624.       space_left = screen_width - used;
  3625.  
  3626.       if(space_left < 0){
  3627.     /*
  3628.      * If they're all percentages, and the percentages add up to 100,
  3629.      * then we should fix the rounding problem.
  3630.      */
  3631.     pc_tot = 0;
  3632.     if(all_percents){
  3633.           for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++)
  3634.         if(pab->disp_form[i].wtype == Percent)
  3635.           pc_tot += pab->disp_form[i].req_width;
  3636.     }
  3637.  
  3638.     /* fix the rounding problem */
  3639.     if(all_percents && pc_tot <= 100){
  3640.       int col = columns;
  3641.       int this_col = 0;
  3642.       int fix = used - screen_width;
  3643.  
  3644.       while(fix--){
  3645.         if(col < 0)
  3646.           col = columns;
  3647.  
  3648.         /* find col'th column */
  3649.         for(i=0, this_col=0; i < NFIELDS; i++){
  3650.           if(pab->disp_form[i].wtype == Percent){
  3651.             if(col == ++this_col)
  3652.               break;
  3653.           }
  3654.         }
  3655.  
  3656.         pab->disp_form[i].width--;
  3657.         col--;
  3658.       }
  3659.     }
  3660.     /*
  3661.      * Assume they meant to have them add up to over 100%, so we
  3662.      * just truncate the right hand edge.
  3663.      */
  3664.     else{
  3665.       int this_fix, space_over;
  3666.  
  3667.       /* have to reduce space_over down to zero.  */
  3668.       space_over = used - screen_width;
  3669.       for(i=NFIELDS-1; i >= 0 && space_over > 0; i--){
  3670.         if(pab->disp_form[i].type != Notused){
  3671.           this_fix = min(pab->disp_form[i].width, space_over);
  3672.           pab->disp_form[i].width -= this_fix;
  3673.           space_over -= this_fix;
  3674.         }
  3675.       }
  3676.     }
  3677.       }
  3678.       else if(space_left > 0){
  3679.     if(some_to_calculate){
  3680.       /* make nickname big enough to show all nicknames */
  3681.       if(nick >= 0 && pab->disp_form[nick].wtype == WeCalculate){
  3682.         --some_to_calculate;
  3683.         if(pab->disp_form[nick].width != max_nick){
  3684.           int this_fix;
  3685.  
  3686.           this_fix = min(max_nick-pab->disp_form[nick].width, space_left);
  3687.           pab->disp_form[nick].width += this_fix;
  3688.           space_left -= this_fix;
  3689.         }
  3690.       }
  3691.  
  3692.       if(!some_to_calculate && space_left > 0)
  3693.         goto none_to_calculate;
  3694.  
  3695.       if(space_left > 0){
  3696.         int weight  = 0;
  3697.         int used_wt = 0;
  3698.  
  3699.         /* add up total weight */
  3700.         for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
  3701.           if(i != nick && pab->disp_form[i].wtype == WeCalculate){
  3702.             switch(pab->disp_form[i].type){
  3703.           case Fullname:
  3704.             weight  += max(third_full, pab->disp_form[i].width);
  3705.             used_wt += pab->disp_form[i].width;
  3706.             break;
  3707.  
  3708.           case Addr:
  3709.             weight  += max(third_addr, pab->disp_form[i].width);
  3710.             used_wt += pab->disp_form[i].width;
  3711.             break;
  3712.  
  3713.           case Filecopy:
  3714.             weight  += max(third_fcc, pab->disp_form[i].width);
  3715.             used_wt += pab->disp_form[i].width;
  3716.             break;
  3717.         }
  3718.           }
  3719.         }
  3720.  
  3721.         if(weight > 0){
  3722.           int this_fix;
  3723.  
  3724.           if(weight - used_wt <= space_left){
  3725.             for(i = 0;
  3726.                 i < NFIELDS && pab->disp_form[i].type != Notused;
  3727.             i++){
  3728.           if(i != nick && pab->disp_form[i].wtype == WeCalculate){
  3729.             switch(pab->disp_form[i].type){
  3730.               case Fullname:
  3731.                 this_fix = third_full - pab->disp_form[i].width;
  3732.                 space_left -= this_fix;
  3733.             pab->disp_form[i].width += this_fix;
  3734.             break;
  3735.  
  3736.               case Addr:
  3737.             this_fix = third_addr - pab->disp_form[i].width;
  3738.             space_left -= this_fix;
  3739.             pab->disp_form[i].width += this_fix;
  3740.             break;
  3741.  
  3742.               case Filecopy:
  3743.             this_fix = third_fcc - pab->disp_form[i].width;
  3744.             space_left -= this_fix;
  3745.             pab->disp_form[i].width += this_fix;
  3746.             break;
  3747.             }
  3748.           }
  3749.         }
  3750.  
  3751.         /* if still space left and a comment field, all to comment */
  3752.         if(space_left){
  3753.               for(i = 0;
  3754.               i < NFIELDS && pab->disp_form[i].type != Notused;
  3755.               i++){
  3756.             if(pab->disp_form[i].type == Comment &&
  3757.                pab->disp_form[i].wtype == WeCalculate){
  3758.               pab->disp_form[i].width += space_left;
  3759.               space_left = 0;
  3760.             }
  3761.           }
  3762.         }
  3763.           }
  3764.           else{ /* not enough space, dole out weighted pieces */
  3765.         int was_sl = space_left;
  3766.  
  3767.             for(i = 0;
  3768.             i < NFIELDS && pab->disp_form[i].type != Notused;
  3769.             i++){
  3770.           if(i != nick && pab->disp_form[i].wtype == WeCalculate){
  3771.             switch(pab->disp_form[i].type){
  3772.               case Fullname:
  3773.             /* round down */
  3774.             this_fix = (third_full * was_sl)/weight;
  3775.             space_left -= this_fix;
  3776.             pab->disp_form[i].width += this_fix;
  3777.             break;
  3778.  
  3779.               case Addr:
  3780.             this_fix = (third_addr * was_sl)/weight;
  3781.             space_left -= this_fix;
  3782.             pab->disp_form[i].width += this_fix;
  3783.             break;
  3784.  
  3785.               case Filecopy:
  3786.             this_fix = (third_fcc * was_sl)/weight;
  3787.             space_left -= this_fix;
  3788.             pab->disp_form[i].width += this_fix;
  3789.             break;
  3790.             }
  3791.           }
  3792.         }
  3793.           }
  3794.         }
  3795.  
  3796.         /* give out rest */
  3797.         while(space_left > 0){
  3798.           for(i=NFIELDS-1; i >= 0 && space_left > 0; i--){
  3799.             if(i != nick && pab->disp_form[i].wtype == WeCalculate){
  3800.           pab->disp_form[i].width++;
  3801.           space_left--;
  3802.         }
  3803.           }
  3804.         }
  3805.       }
  3806.     }
  3807.     else{
  3808.       /*
  3809.        * If they're all percentages, and the percentages add up to 100,
  3810.        * then we just have to fix a rounding problem.  Otherwise, we'll
  3811.        * assume the user meant to have them add up to less than 100.
  3812.        */
  3813. none_to_calculate:
  3814.       pc_tot = 0;
  3815.       if(all_percents){
  3816.         for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++)
  3817.           if(pab->disp_form[i].wtype == Percent)
  3818.             pc_tot += pab->disp_form[i].req_width;
  3819.       }
  3820.  
  3821.       if(all_percents && pc_tot >= 100){
  3822.         int col = columns;
  3823.         int this_col = 0;
  3824.         int fix = screen_width - used;
  3825.  
  3826.         while(fix--){
  3827.           if(col < 0)
  3828.             col = columns;
  3829.  
  3830.           /* find col'th column */
  3831.           for(i=0, this_col=0; i < NFIELDS; i++){
  3832.             if(pab->disp_form[i].wtype == Percent){
  3833.               if(col == ++this_col)
  3834.             break;
  3835.         }
  3836.           }
  3837.  
  3838.           pab->disp_form[i].width++;
  3839.           col--;
  3840.         }
  3841.       }
  3842.       /* else, user specified less than 100%, leave it */
  3843.     }
  3844.       }
  3845.       /* else space_left == zero, nothing to do */
  3846.  
  3847.       /*
  3848.        * Check for special case.  If we find it, this is the case where
  3849.        * we want to display the list entry field even though there is no
  3850.        * address field displayed.  All of the display width is probably
  3851.        * used up by now, so we just need to pick some arbitrary width
  3852.        * for these lines.  Since these lines are separate from the other
  3853.        * lines we've been calculating, we don't have to worry about running
  3854.        * into them, except for list continuation lines which we're not
  3855.        * going to worry about.
  3856.        */
  3857.       for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
  3858.     if(pab->disp_form[i].wtype == Special){
  3859.         pab->disp_form[i].width = min(strlen(CLICKHERE), screen_width);
  3860.         break;
  3861.     }
  3862.       }
  3863.     }
  3864.  
  3865.     /* check for width changes */
  3866.     for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
  3867.       if(pab->disp_form[i].width != pab->disp_form[i].old_width){
  3868.     ret++;  /* Tell the caller the screen changed */
  3869.     pab->disp_form[i].old_width = pab->disp_form[i].width;
  3870.       }
  3871.     }
  3872.  
  3873.     pab->nick_is_displayed    = 0;
  3874.     pab->full_is_displayed    = 0;
  3875.     pab->addr_is_displayed    = 0;
  3876.     pab->fcc_is_displayed     = 0;
  3877.     pab->comment_is_displayed = 0;
  3878.     for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
  3879.       if(pab->disp_form[i].width > 0){
  3880.         switch(pab->disp_form[i].type){
  3881.       case Nickname:
  3882.         pab->nick_is_displayed++;
  3883.         break;
  3884.       case Fullname:
  3885.         pab->full_is_displayed++;
  3886.         break;
  3887.       case Addr:
  3888.         pab->addr_is_displayed++;
  3889.         break;
  3890.       case Filecopy:
  3891.         pab->fcc_is_displayed++;
  3892.         break;
  3893.       case Comment:
  3894.         pab->comment_is_displayed++;
  3895.         break;
  3896.     }
  3897.       }
  3898.     }
  3899.   }
  3900.  
  3901.   return(ret);
  3902. }
  3903.  
  3904.  
  3905. void
  3906. redraw_addr_screen()
  3907. {
  3908.     dprint(7, (debugfile, "- redraw_addr_screen -\n"));
  3909.  
  3910.     ab_resize();
  3911.     if(as.l_p_page <= 0)
  3912.       return;
  3913.  
  3914.     (void)calculate_field_widths();
  3915.     display_book(0, as.cur_row, -1, 1, (Pos *)NULL);
  3916. }
  3917.  
  3918.  
  3919. /*
  3920.  * Little front end for address book screen so it can be called out
  3921.  * of the main command loop in pine.c
  3922.  */
  3923. void
  3924. addr_book_screen(pine_state)
  3925.     struct pine *pine_state;
  3926. {
  3927.     dprint(1, (debugfile, "\n\n --- ADDR_BOOK_SCREEN ---\n\n"));
  3928.  
  3929.     mailcap_free(); /* free resources we won't be using for a while */
  3930.  
  3931.     if(setjmp(addrbook_changed_unexpectedly)){
  3932.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  3933.     dprint(1, (debugfile, "RESETTING address book... addr_book_screen!\n"));
  3934.     addrbook_reset();
  3935.     }
  3936.  
  3937.     (void)addr_book(AddrBookScreen, "ADDRESS BOOK", NULL);
  3938.     end_adrbks();
  3939.     pine_state->prev_screen = addr_book_screen;
  3940. }
  3941.  
  3942.  
  3943. /*ARGSUSED*/
  3944. /*
  3945.  * Call address book from message composer
  3946.  *
  3947.  * Args: error_mess -- pointer to return error messages in (unused here)
  3948.  *
  3949.  * Returns: pointer to returned address, or NULL if nothing returned
  3950.  */
  3951. char *
  3952. addr_book_compose(error_mess)
  3953.     char **error_mess;
  3954. {
  3955.     char *p;
  3956.     jmp_buf        save_jmp_buf;
  3957.  
  3958.     dprint(1, (debugfile, "--- addr_book_compose ---\n"));
  3959.  
  3960.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  3961.     if(setjmp(addrbook_changed_unexpectedly)){
  3962.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  3963.     dprint(1,
  3964.         (debugfile, "RESETTING address book... addr_book_compose!\n"));
  3965.     addrbook_reset();
  3966.     }
  3967.  
  3968.     /*
  3969.      * We used to use SelectAddrCom here, but we changed it so that it
  3970.      * returns a list of nicknames instead of the expanded addresses.  That
  3971.      * allows the composer to then call build_address and associate the
  3972.      * correct fcc with a nickname.  Previously, the composer didn't call
  3973.      * the builder after doing a selector call.
  3974.      */
  3975.     p = addr_book(SelectNicksCom, "COMPOSER: SELECT ADDRESS", NULL);
  3976.  
  3977.     end_adrbks();
  3978.  
  3979.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  3980.     return(p);
  3981. }
  3982.  
  3983.  
  3984. /*ARGSUSED*/
  3985. /*
  3986.  * Call address book from message composer for Lcc line
  3987.  *
  3988.  * Args: error_mess -- pointer to return error messages in (unused here)
  3989.  *
  3990.  * Returns: pointer to returned address, or NULL if nothing returned
  3991.  */
  3992. char *
  3993. addr_book_compose_lcc(error_mess)
  3994.     char **error_mess;
  3995. {
  3996.     char *p;
  3997.     jmp_buf        save_jmp_buf;
  3998.  
  3999.     dprint(1, (debugfile, "--- addr_book_compose_lcc ---\n"));
  4000.  
  4001.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4002.     if(setjmp(addrbook_changed_unexpectedly)){
  4003.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4004.     dprint(1,
  4005.         (debugfile, "RESETTING address book... addr_book_compose_lcc!\n"));
  4006.     addrbook_reset();
  4007.     }
  4008.  
  4009.     p = addr_book(SelectAddrLccCom, "COMPOSER: SELECT LIST", NULL);
  4010.  
  4011.     end_adrbks();
  4012.  
  4013.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4014.     return(p);
  4015. }
  4016.  
  4017.  
  4018. /*ARGSUSED*/
  4019. /*
  4020.  * Call address book from message composer for Lcc line
  4021.  *
  4022.  * Args: error_mess -- pointer to return error messages in (unused here)
  4023.  *
  4024.  * Returns: pointer to returned address, or NULL if nothing returned
  4025.  */
  4026. char *
  4027. addr_book_change_list(error_mess)
  4028.     char **error_mess;
  4029. {
  4030.     char *p;
  4031.     jmp_buf        save_jmp_buf;
  4032.  
  4033.     dprint(1, (debugfile, "--- addr_book_change_list ---\n"));
  4034.  
  4035.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4036.     if(setjmp(addrbook_changed_unexpectedly)){
  4037.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4038.     dprint(1,
  4039.         (debugfile, "RESETTING address book... addr_book_change_list!\n"));
  4040.     addrbook_reset();
  4041.     }
  4042.  
  4043.     p = addr_book(SelectNicksCom, "ADDRESS BOOK (Edit): SELECT ADDRESSES",
  4044.             NULL);
  4045.  
  4046.     end_adrbks();
  4047.  
  4048.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4049.     return(p);
  4050. }
  4051.  
  4052.  
  4053. /*ARGSUSED*/
  4054. /*
  4055.  * Call address book from pine_simple_send
  4056.  *
  4057.  * Returns: pointer to returned address, or NULL if nothing returned
  4058.  */
  4059. char *
  4060. addr_book_bounce()
  4061. {
  4062.     return(addr_book_seladdr());
  4063. }
  4064.  
  4065.  
  4066. /*
  4067.  * Call address book from take address screen
  4068.  *
  4069.  * Returns: pointer to returned nickname, or NULL if nothing returned
  4070.  *
  4071.  * The caller is assumed to handle the closing of the addrbooks, so we
  4072.  * don't call end_adrbks().
  4073.  */
  4074. char *
  4075. addr_book_takeaddr()
  4076. {
  4077.     char *p;
  4078.     jmp_buf        save_jmp_buf;
  4079.  
  4080.     dprint(1, (debugfile, "- addr_book_takeaddr -\n"));
  4081.  
  4082.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4083.     if(setjmp(addrbook_changed_unexpectedly)){
  4084.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4085.     dprint(1,
  4086.         (debugfile, "RESETTING address book...addr_book_takeaddr!\n"));
  4087.     addrbook_reset();
  4088.     }
  4089.  
  4090.     p = addr_book(SelectNickTake, "TAKEADDR: SELECT NICKNAME", NULL);
  4091.  
  4092.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4093.     return(p);
  4094. }
  4095.  
  4096.  
  4097. /*
  4098.  * Call address book from editing screen for nickname field.
  4099.  *
  4100.  * Returns: pointer to returned nickname, or NULL if nothing returned
  4101.  *
  4102.  * The caller is assumed to handle the closing of the addrbooks, so we
  4103.  * don't call end_adrbks().
  4104.  */
  4105. char *
  4106. addr_book_nick_for_edit()
  4107. {
  4108.     char *p;
  4109.     jmp_buf        save_jmp_buf;
  4110.  
  4111.     dprint(1, (debugfile, "- addr_book_nick_for_edit -\n"));
  4112.  
  4113.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4114.     if(setjmp(addrbook_changed_unexpectedly)){
  4115.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4116.     dprint(1,
  4117.         (debugfile, "RESETTING address book...addr_book_nick_for_edit!\n"));
  4118.     addrbook_reset();
  4119.     }
  4120.  
  4121.     p = addr_book(SelectNickCom, "SELECT NICKNAME", NULL);
  4122.  
  4123.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4124.     return(p);
  4125. }
  4126.  
  4127.  
  4128. /*
  4129.  * Call address book for generic nickname select
  4130.  *
  4131.  * Returns: pointer to returned nickname, or NULL if nothing returned
  4132.  *
  4133.  * The caller is assumed to handle the closing of the addrbooks, so we
  4134.  * don't call end_adrbks().
  4135.  */
  4136. char *
  4137. addr_book_selnick()
  4138. {
  4139.     char *p;
  4140.     jmp_buf        save_jmp_buf;
  4141.  
  4142.     dprint(1, (debugfile, "- addr_book_selnick -\n"));
  4143.  
  4144.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4145.     if(setjmp(addrbook_changed_unexpectedly)){
  4146.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4147.     dprint(1,
  4148.         (debugfile, "RESETTING address book...addr_book_selnick!\n"));
  4149.     addrbook_reset();
  4150.     }
  4151.  
  4152.     p = addr_book(SelectNick, "SELECT NICKNAME", NULL);
  4153.  
  4154.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4155.     return(p);
  4156. }
  4157.  
  4158.  
  4159. /*
  4160.  * Call address book for generic address select
  4161.  *
  4162.  * Returns: pointer to returned address, or NULL if nothing returned
  4163.  *
  4164.  * The caller is assumed to handle the closing of the addrbooks, so we
  4165.  * don't call end_adrbks().
  4166.  */
  4167. char *
  4168. addr_book_seladdr()
  4169. {
  4170.     char *p;
  4171.     jmp_buf        save_jmp_buf;
  4172.  
  4173.     dprint(1, (debugfile, "- addr_book_seladdr -\n"));
  4174.  
  4175.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4176.     if(setjmp(addrbook_changed_unexpectedly)){
  4177.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4178.     dprint(1,
  4179.         (debugfile, "RESETTING address book...addr_book_seladdr!\n"));
  4180.     addrbook_reset();
  4181.     }
  4182.  
  4183.     p = addr_book(SelectAddr, "SELECT ADDRESS", NULL);
  4184.  
  4185.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4186.     return(p);
  4187. }
  4188.  
  4189.  
  4190. /*
  4191.  * Call address book for address select
  4192.  *
  4193.  * Returns: pointer to returned address without the fullname,
  4194.  *          or NULL if nothing returned
  4195.  *
  4196.  * The caller is assumed to handle the closing of the addrbooks, so we
  4197.  * don't call end_adrbks().
  4198.  */
  4199. char *
  4200. addr_book_seladdr_nofull()
  4201. {
  4202.     char *p;
  4203.     jmp_buf        save_jmp_buf;
  4204.  
  4205.     dprint(1, (debugfile, "- addr_book_seladdr_nofull -\n"));
  4206.  
  4207.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4208.     if(setjmp(addrbook_changed_unexpectedly)){
  4209.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4210.     dprint(1,
  4211.         (debugfile,"RESETTING address book...addr_book_seladdr_nofull!\n"));
  4212.     addrbook_reset();
  4213.     }
  4214.  
  4215.     p = addr_book(SelectAddrTake, "SELECT ADDRESS", NULL);
  4216.  
  4217.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4218.     return(p);
  4219. }
  4220.  
  4221.  
  4222. /*
  4223.  * Call address book for selecting a list of nicknames
  4224.  *
  4225.  * Returns: array is returned in argument
  4226.  *
  4227.  * The caller is assumed to handle the closing of the addrbooks, so we
  4228.  * don't call end_adrbks().
  4229.  */
  4230. void
  4231. addr_book_manynicks(return_array)
  4232.     char ***return_array;
  4233. {
  4234.     jmp_buf        save_jmp_buf;
  4235.  
  4236.     dprint(1, (debugfile, "- addr_book_manynicks -\n"));
  4237.  
  4238.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  4239.     if(setjmp(addrbook_changed_unexpectedly)){
  4240.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  4241.     dprint(1,
  4242.         (debugfile, "RESETTING address book...addr_book_manynicks!\n"));
  4243.     addrbook_reset();
  4244.     }
  4245.  
  4246.     (void)addr_book(SelectManyNicks, "SELECT NICKNAMES", return_array);
  4247.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  4248. }
  4249.  
  4250.  
  4251. static struct key ab_keys[] =
  4252.      {{"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  4253.       {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  4254.       {"P","PrevEntry",KS_NONE},    {"N","NextEntry",KS_NONE},
  4255.       {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  4256.       {"D","Delete",KS_DELETE},        {"A","AddNew",KS_NONE},
  4257.       {"C","ComposeTo",KS_COMPOSER},    {"W","WhereIs",KS_WHEREIS},
  4258.       {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  4259.       {"Q","Quit",KS_EXIT},        {NULL,NULL,KS_NONE},
  4260.       {"L","ListFldrs",KS_FLDRLIST},    {"G","GotoFldr",KS_GOTOFLDR},
  4261.       {"I","Index",KS_FLDRINDEX},    {NULL,NULL,KS_NONE},
  4262.       {"Y","prYnt",KS_PRINT},        {"T","TakeAddr",KS_TAKEADDR},
  4263.       {"X","eXport",KS_EXPORT},        {"F","Forward",KS_NONE}};
  4264. INST_KEY_MENU(ab_keymenu, ab_keys);
  4265. #define OTHER_KEY  1
  4266. #define MAIN_KEY   2
  4267. #define SELECT_KEY 3
  4268. #define DELETE_KEY 8
  4269. #define ADD_KEY    9
  4270. #define SENDTO_KEY 10
  4271. #define TAKE_KEY   21
  4272. #define FORW_KEY   23
  4273.  
  4274.  
  4275. /*
  4276.  *  Main address book screen 
  4277.  *
  4278.  * Control loop for address book.  Commands are executed out of a big
  4279.  * switch and screen painting is done.
  4280.  * The argument controls whether or not it is called to return an address
  4281.  * to the composer, a nickname to the TakeAddr screen, or just for address
  4282.  * book maintenance.
  4283.  *
  4284.  * Args: style -- how we were called
  4285.  *
  4286.  * Return: might return a string for the composer to edit (if SelectAddrCom
  4287.  * style)
  4288.  *         or a nickname (if SelectNick),
  4289.  *       or an array of nicknames (if SelectManyNicks).
  4290.  */
  4291. char *
  4292. addr_book(style, title, return_array)
  4293.     AddrBookArg style;
  4294.     char       *title;
  4295.     char     ***return_array;
  4296. {
  4297.     int             r, c, orig_c, i,
  4298.              command_line,
  4299.              did_delete_flag,
  4300.                      quit,           /* loop control                         */
  4301.                      km_popped,      /* menu is popped up in blank menu mode */
  4302.              current_changed_flag,  /* only current row needs update */
  4303.              was_clickable_last_time, /* on CLICKHERE last time thru */
  4304.              start_disp,     /* Paint from this line down (0 is top) */
  4305.              rdonly,         /* cur addrbook read only               */
  4306.              empty,          /* cur addrbook empty                   */
  4307.              are_selecting,  /* called as ^T selector                */
  4308.              from_composer,  /* from composer                        */
  4309.              listmode_ok,    /* ok to do ListMode with this style    */
  4310.              selecting_nick, /* selecting nickname(s)                */
  4311.              selecting_one_nick,
  4312.              selecting_mult_nicks,
  4313.              no_fullname,    /* return address without fullname      */
  4314.              checkedn,       /* how many are checked                 */
  4315.              def_cmd,        /* default command                      */
  4316.                      warped;         /* we warped through hyperspace to a
  4317.                         new location in the display list     */
  4318.     long         fl,
  4319.              new_ent,
  4320.              new_top_ent,    /* entry on top of screen after oper    */
  4321.              new_line;       /* new line number after operation      */
  4322.     char            *addr;
  4323.     bitmap_t         bitmap;
  4324.     struct key_menu *km;
  4325.     OtherMenu        what;
  4326.     PerAddrBook     *pab;
  4327.     AddrScrn_Disp   *dl;
  4328.     struct pine     *ps;
  4329.     Pos              cursor_pos;
  4330.  
  4331.  
  4332.     dprint(1, (debugfile, "--- addr_book ---  (%s)\n",
  4333.           style==AddrBookScreen      ? "AddrBookScreen"            :
  4334.            style==SelectAddrCom       ? "SelectAddrCom"            :
  4335.             style==SelectAddrLccCom    ? "SelectAddrLccCom"        :
  4336.              style==SelectNicksCom      ? "SelectNicksCom"         :
  4337.               style==SelectAddr          ? "SelectAddr"            :
  4338.                style==SelectAddrTake      ? "SelectAddrTake"       :
  4339.             style==SelectAddrNoFullCom ? "SelectAddrNoFullCom" :
  4340.              style==SelectNick          ? "SelectNick"         :
  4341.               style==SelectNickTake      ? "SelectNickTake"    :
  4342.                style==SelectNickCom       ? "SelectNickCom"    :
  4343.                 style==SelectManyNicks     ? "SelectManyNicks" :
  4344.                                               "UnknownStyle"));
  4345.  
  4346.     ps = ps_global;
  4347.     km = &ab_keymenu;
  4348.  
  4349.     from_composer  = (style == SelectAddrCom || style == SelectAddrLccCom
  4350.               || style == SelectNicksCom || style == SelectNickCom
  4351.               || style == SelectAddrNoFullCom);
  4352.     are_selecting  = (style != AddrBookScreen);
  4353.     selecting_one_nick = (style == SelectNick || style == SelectNickTake
  4354.                   || style == SelectNickCom);
  4355.     selecting_mult_nicks = (style == SelectAddrLccCom
  4356.                 || style == SelectNicksCom
  4357.                 || style == SelectManyNicks);
  4358.     selecting_nick = selecting_one_nick || selecting_mult_nicks;
  4359.     listmode_ok    = (style == SelectAddrCom || style == SelectAddrLccCom
  4360.               || style == SelectNicksCom || style == SelectManyNicks);
  4361.     no_fullname    = (style == SelectAddrTake || style == SelectAddrNoFullCom);
  4362.     as.checkboxes  = (style == SelectManyNicks);  /* auto ListMode */
  4363.  
  4364.     def_cmd = F_ON(F_USE_FK,ps_global) ? PF4 : are_selecting ? 's' : 'c';
  4365.  
  4366.     /* Coming in from the composer, may need to reset the window */
  4367.     if(from_composer){
  4368.     fix_windsize(ps_global);
  4369.     init_sigwinch();
  4370.     mark_status_dirty();
  4371.     mark_titlebar_dirty();
  4372.     mark_keymenu_dirty();
  4373.     }
  4374.  
  4375.     command_line = -FOOTER_ROWS(ps_global); /* third line from the bottom */
  4376.     what         = FirstMenu;
  4377.  
  4378.     if(!init_addrbooks(HalfOpen, 1, 1, !are_selecting)){
  4379.     q_status_message(SM_ORDER | SM_DING,3,4,"No Address Book Configured");
  4380.     if(!are_selecting)
  4381.       ps_global->next_screen = ps_global->prev_screen;
  4382.  
  4383.         return NULL;
  4384.     }
  4385.  
  4386.  
  4387.     for(i = 0; i < as.n_addrbk; i++){
  4388.     pab = &as.adrbks[i];
  4389.     /* nothing checked to start */
  4390.     if(pab->address_book && pab->address_book->checks)
  4391.       checked_free(pab->address_book->checks);
  4392.  
  4393.     init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
  4394.     }
  4395.  
  4396.     (void)calculate_field_widths();
  4397.  
  4398.     quit                     = 0;
  4399.     km_popped             = 0;
  4400.     ps->mangled_screen       = 1;
  4401.     current_changed_flag     = 0;
  4402.     start_disp               = 0;
  4403.     was_clickable_last_time  = 0; /* doesn't matter what it is */
  4404.     checkedn                 = 0;
  4405.  
  4406.     c = 'x'; /* For display_message the first time through */
  4407.  
  4408.  
  4409.     while(!quit){
  4410.     if(km_popped){
  4411.         km_popped--;
  4412.         if(km_popped == 0){
  4413.         clearfooter(ps_global);
  4414.         /*
  4415.          * Have to repaint from earliest change down, including
  4416.          * at least the last two body lines.
  4417.          */
  4418.         if(ps_global->mangled_body) /* it was already mangled */
  4419.           start_disp = min(start_disp, as.l_p_page -2);
  4420.         else if(current_changed_flag){
  4421.             ps_global->mangled_body = 1;
  4422.             start_disp = min(min(as.cur_row, as.l_p_page -2),
  4423.                     as.old_cur_row);
  4424.         }
  4425.         else{
  4426.             ps_global->mangled_body = 1;
  4427.             start_disp = as.l_p_page -2;
  4428.         }
  4429.         }
  4430.     }
  4431.  
  4432.     ps_global->redrawer = redraw_addr_screen;
  4433.  
  4434.         if(new_mail(0, c == NO_OP_IDLE ? 0 : 2, 1) >= 0)
  4435.       ps->mangled_header = 1;
  4436.  
  4437.         if(streams_died())
  4438.           ps->mangled_header = 1;
  4439.  
  4440.     if(ps->mangled_screen){
  4441.         ps->mangled_header   = 1;
  4442.         ps->mangled_body     = 1;
  4443.         ps->mangled_footer   = 1;
  4444.         start_disp           = 0;
  4445.         current_changed_flag = 0;
  4446.         ps->mangled_screen   = 0;
  4447.     }
  4448.  
  4449.     if(ps->mangled_header){
  4450.             set_titlebar(title, ps_global->mail_stream,
  4451.                          ps_global->context_current, ps_global->cur_folder,
  4452.                          ps_global->msgmap, 1,
  4453.                          FolderName, 0, 0);
  4454.         ps->mangled_header = 0;
  4455.     }
  4456.  
  4457.     if(ps->mangled_body){
  4458.         int old_cur;
  4459.  
  4460.         if(calculate_field_widths())
  4461.           start_disp = 0;
  4462.  
  4463.         display_book(start_disp,
  4464.              as.cur_row,  
  4465.              as.old_cur_row,  
  4466.              1,
  4467.              &cursor_pos);
  4468.  
  4469.         as.old_cur_row   = as.cur_row;
  4470.         ps->mangled_body = 0;
  4471.             start_disp       = 0;
  4472.         old_cur          = as.cur;
  4473.         as.cur           = cur_addr_book();
  4474.         pab              = &as.adrbks[as.cur];
  4475.         if(as.cur != old_cur)
  4476.           q_status_message1(SM_ORDER, 0, 2, "Now in addressbook %s",
  4477.                         pab->nickname);
  4478. #ifdef    _WINDOWS
  4479.         {
  4480.         long i;
  4481.  
  4482.         for(i = as.top_ent; dlist(i)->type != End; i++)
  4483.           ;
  4484.  
  4485.         scroll_setrange((as.last_ent = i) - 1L);
  4486.         }
  4487. #endif
  4488.     }
  4489.     /* current entry has been changed */
  4490.     else if(current_changed_flag){
  4491.         int old_cur;
  4492.         int need_redraw;
  4493.  
  4494.         need_redraw = calculate_field_widths();
  4495.  
  4496.         /*---------- Update the current entry, (move or change) -------*/
  4497.         display_book(need_redraw ? 0 : as.cur_row,
  4498.              as.cur_row,  
  4499.              as.old_cur_row,  
  4500.              need_redraw,
  4501.              &cursor_pos);
  4502.  
  4503.         as.old_cur_row       = as.cur_row;
  4504.         current_changed_flag = 0;
  4505.         old_cur              = as.cur;
  4506.         as.cur               = cur_addr_book();
  4507.         pab                  = &as.adrbks[as.cur];
  4508.         if(as.cur != old_cur)
  4509.           q_status_message1(SM_ORDER, 0, 2, "Now in addressbook %s",
  4510.                         pab->nickname);
  4511.         }
  4512.  
  4513.     dprint(9, (debugfile, "addr_book: top of loop, addrbk %d top_ent %ld cur_row %d\n", as.cur, as.top_ent, as.cur_row));
  4514.  
  4515.     /*
  4516.      * This is a check to catch the case where we move from a non-
  4517.      * clickable row into a clickable row or vice versa.  That means
  4518.      * the footer changes.
  4519.      */
  4520.     if(!ps->mangled_footer && ((was_clickable_last_time &&
  4521.         !entry_is_clickable(as.top_ent+as.cur_row)) ||
  4522.        (!was_clickable_last_time &&
  4523.         entry_is_clickable(as.top_ent+as.cur_row))))
  4524.         ps->mangled_footer = 1;
  4525.  
  4526.     was_clickable_last_time = entry_is_clickable(as.top_ent+as.cur_row);
  4527.  
  4528.         if(ps->mangled_footer){
  4529.  
  4530.         setbitmap(bitmap);
  4531.         if(are_selecting){
  4532.         km->how_many = 1;
  4533.         ab_keys[MAIN_KEY].name   = "E";
  4534.         ab_keys[MAIN_KEY].label  = "ExitSelect";
  4535.         KS_OSDATASET(&ab_keys[MAIN_KEY], KS_EXITMODE);
  4536.         ab_keys[SELECT_KEY].name  = "S";
  4537.         ab_keys[SELECT_KEY].label = "[Select]";
  4538.         def_cmd = F_ON(F_USE_FK,ps_global) ? PF4 : 's';
  4539.         KS_OSDATASET(&ab_keys[SELECT_KEY], KS_NONE);
  4540.         clrbitn(OTHER_KEY, bitmap);
  4541.         clrbitn(TAKE_KEY, bitmap);
  4542.         clrbitn(FORW_KEY, bitmap);
  4543.         clrbitn(ADD_KEY, bitmap);
  4544.         clrbitn(SENDTO_KEY, bitmap);
  4545.         KS_OSDATASET(&ab_keys[DELETE_KEY], KS_NONE);
  4546.         if(as.checkboxes){
  4547.             ab_keys[DELETE_KEY].name  = "X";
  4548.             ab_keys[DELETE_KEY].label = "[Set/Unset]";
  4549.             ab_keys[SELECT_KEY].label = "Select";
  4550.             def_cmd = F_ON(F_USE_FK,ps_global) ? PF9 : 'x';
  4551.             if(entry_is_clickable(as.top_ent+as.cur_row)){
  4552.             def_cmd = F_ON(F_USE_FK,ps_global) ? PF4 : 's';
  4553.             ab_keys[DELETE_KEY].label = "Set/Unset";
  4554.             ab_keys[SELECT_KEY].label = "[Select]";
  4555.             }
  4556.         }
  4557.         else if(listmode_ok){
  4558.             ab_keys[DELETE_KEY].name   = "L";
  4559.             ab_keys[DELETE_KEY].label  = "ListMode";
  4560.         }
  4561.         else
  4562.           clrbitn(DELETE_KEY, bitmap);
  4563.         }
  4564.         else{
  4565.         km->how_many = 2;
  4566.         ab_keys[MAIN_KEY].name   = "M";
  4567.         ab_keys[MAIN_KEY].label  = "Main Menu";
  4568.         KS_OSDATASET(&ab_keys[MAIN_KEY], KS_MAINMENU);
  4569.         if(entry_is_clickable(as.top_ent+as.cur_row)){
  4570.             ab_keys[SELECT_KEY].name  = "S";
  4571.             ab_keys[SELECT_KEY].label = "[Select]";
  4572.             def_cmd = F_ON(F_USE_FK,ps_global) ? PF4 : 's';
  4573.             KS_OSDATASET(&ab_keys[SELECT_KEY], KS_NONE);
  4574.         }
  4575.         else{
  4576.             ab_keys[SELECT_KEY].name  = "V";
  4577.             ab_keys[SELECT_KEY].label = "[View/Edit]";
  4578.             def_cmd = F_ON(F_USE_FK,ps_global) ? PF4 : 'v';
  4579.             KS_OSDATASET(&ab_keys[SELECT_KEY], KS_NONE);
  4580.         }
  4581.  
  4582.         ab_keys[DELETE_KEY].name   = "D";
  4583.         ab_keys[DELETE_KEY].label  = "Delete";
  4584.         KS_OSDATASET(&ab_keys[DELETE_KEY], KS_DELETE);
  4585.         if(was_clickable_last_time)  /* it's still *this* time now */
  4586.           clrbitn(SENDTO_KEY, bitmap);
  4587.         }
  4588.  
  4589.         if(km_popped){
  4590.         FOOTER_ROWS(ps_global) = 3;
  4591.         clearfooter(ps_global);
  4592.         }
  4593.  
  4594.         draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
  4595.                    1-FOOTER_ROWS(ps_global), 0, what, 0);
  4596.         ps->mangled_footer = 0;
  4597.         what               = SameTwelve;
  4598.         if(km_popped){
  4599.         FOOTER_ROWS(ps_global) = 1;
  4600.         mark_keymenu_dirty();
  4601.         }
  4602.     }
  4603.  
  4604.     rdonly   = (pab->access == ReadOnly);
  4605.     empty    = is_empty(as.cur_row+as.top_ent);
  4606.     if(as.no_op_possbl){
  4607.         q_status_message(SM_ORDER | SM_DING, 0, 4,
  4608.         "No address book operations possible");
  4609.     }
  4610.  
  4611.     /*------------ display any status messages ------------------*/
  4612.     if(km_popped){
  4613.         FOOTER_ROWS(ps_global) = 3;
  4614.         mark_status_unknown();
  4615.     }
  4616.  
  4617.     display_message(c);
  4618.     if(km_popped){
  4619.         FOOTER_ROWS(ps_global) = 1;
  4620.         mark_status_unknown();
  4621.     }
  4622.  
  4623.     if(F_OFF(F_SHOW_CURSOR, ps_global)){
  4624.         /* reset each time through to catch screen size changes */
  4625.         cursor_pos.row =ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global);
  4626.         cursor_pos.col = 0;
  4627.     }
  4628.  
  4629.     MoveCursor(cursor_pos.row, cursor_pos.col);
  4630.  
  4631.  
  4632.     /*---------------- Get command and validate -------------------*/
  4633. #ifdef    MOUSE
  4634.     mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
  4635.     register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
  4636.                ps_global->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
  4637.                ps_global->ttyo->screen_cols);
  4638. #endif
  4639. #ifdef    _WINDOWS
  4640.     mswin_setscrollcallback(addr_scroll_callback);
  4641. #endif
  4642.     c = read_command();
  4643. #ifdef    MOUSE
  4644.     clear_mfunc(mouse_in_content);
  4645. #endif
  4646. #ifdef    _WINDOWS
  4647.     mswin_setscrollcallback(NULL);
  4648. #endif
  4649.         orig_c = c;
  4650.  
  4651.     if(c == ctrl('M') || c == ctrl('J')) /* set up default */
  4652.       c = def_cmd;
  4653.  
  4654.     if(c < 'z' && isupper((unsigned char)c))
  4655.       c = tolower((unsigned char)c);
  4656.  
  4657.     if(km->which == 1 && c >= PF1 && c <= PF12)
  4658.           c = PF2OPF(c);
  4659.  
  4660.         c = validatekeys(c); 
  4661.  
  4662.         dprint(5, (debugfile, "Addrbook command :'%c' (%d)\n", c, c));
  4663.  
  4664.     if(km_popped)
  4665.       switch(c){
  4666.         case NO_OP_IDLE:
  4667.         case NO_OP_COMMAND: 
  4668.         case PF2:
  4669.         case OPF2:
  4670.         case 'o' :
  4671.         case KEY_RESIZE:
  4672.         case ctrl('L'):
  4673.           km_popped++;
  4674.           break;
  4675.         
  4676.         default:
  4677.           clearfooter(ps_global);
  4678.           break;
  4679.       }
  4680.  
  4681.     /*------------- execute command ----------------*/
  4682.     switch(c){
  4683.  
  4684.             /*------------ Noop   (new mail check) --------------*/
  4685.           case NO_OP_IDLE:
  4686.       case NO_OP_COMMAND: 
  4687.         break;
  4688.  
  4689.  
  4690.             /*----------- Help -------------------*/
  4691.       case PF1:
  4692.       case OPF1:
  4693.       case '?':
  4694.       case ctrl('G'):
  4695.         if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
  4696.         km_popped = 2;
  4697.         ps_global->mangled_footer = 1;
  4698.         break;
  4699.         }
  4700.  
  4701.         if(are_selecting){
  4702.         /* single nick select from TakeAddr */
  4703.         if(style == SelectNickTake)
  4704.           helper(h_select_nickname_take, "HELP ON ADDRESS BOOK", 1);
  4705.         /* single nick select from addrbook */
  4706.         else if(selecting_one_nick)
  4707.           helper(h_select_nickname, "HELP ON ADDRESS BOOK", 1);
  4708.         /* can use X checkbox command now */
  4709.         else if(as.checkboxes)
  4710.           helper(h_use_address_bookx, "HELP ON ADDRESS BOOK", 1);
  4711.         /* ListMode command available */
  4712.         else if(listmode_ok)
  4713.           helper(h_use_address_bookl, "HELP ON ADDRESS BOOK", 1);
  4714.         /* no ListMode command available */
  4715.         else
  4716.           helper(h_select_addr, "HELP ON ADDRESS BOOK", 1);
  4717.         }
  4718.         /* general maintenance screen */
  4719.         else{
  4720.         ps_global->next_screen = SCREEN_FUN_NULL;
  4721.         helper(h_address_book, "HELP ON ADDRESS BOOK", 0);
  4722.         }
  4723.  
  4724.         /*
  4725.          * We need this next_screen test in order that helper() can
  4726.          * have a return to Main Menu key.  If helper is called with
  4727.          * a third argument of 1, that isn't one of the possibilities.
  4728.          */
  4729.         if(!are_selecting && ps_global->next_screen != SCREEN_FUN_NULL)
  4730.               quit = 1;
  4731.  
  4732.         ps->mangled_screen = 1;
  4733.         break;
  4734.  
  4735.              
  4736.             /*---------- display other key bindings ------*/
  4737.           case PF2:
  4738.           case OPF2:
  4739.           case 'o' :
  4740.             if(are_selecting)
  4741.               goto bleep;
  4742.  
  4743.             if(c == 'o')
  4744.           warn_other_cmds();
  4745.  
  4746.             what = NextTwelve;
  4747.             ps->mangled_footer = 1;
  4748.             break;
  4749.  
  4750.  
  4751.             /*------------- Back to main menu or exit to caller -------*/
  4752.       case PF3:
  4753.       case 'm':
  4754.       case 'e':
  4755.         if(!are_selecting && c == 'e'){
  4756.             /* backwards compatibility message */
  4757.         q_status_message(SM_ORDER | SM_DING, 0, 2,
  4758.           "Command \"E\" not defined.  Use \"View/Edit\" to edit an entry");
  4759.         break;
  4760.         }
  4761.  
  4762.         if(are_selecting && c == 'm')
  4763.               goto bleep;
  4764.  
  4765.         if(!are_selecting)
  4766.               ps_global->next_screen = main_menu_screen;
  4767.  
  4768.         if(!(are_selecting && as.checkboxes && checkedn > 0)
  4769.            || want_to("Really abandon your selections ",
  4770.               'y', 'x', NO_HELP, 0, 0) == 'y')
  4771.           quit = 1;
  4772.  
  4773.         break;
  4774.  
  4775.  
  4776.             /*------- Select and Edit ---------------*/
  4777.       case PF4:
  4778.       case 's':
  4779.         /* backwards compatibility message */
  4780.         if(c == 's'
  4781.           && !(are_selecting || entry_is_clickable(as.top_ent+as.cur_row))){
  4782.         q_status_message(SM_ORDER | SM_DING, 0, 2,
  4783.          "Command \"S\" not defined.  Use \"AddNew\" to create a list");
  4784.         break;
  4785.         }
  4786.  
  4787.       select:
  4788.         if(c == PF4 && !are_selecting
  4789.            && !entry_is_clickable(as.top_ent+as.cur_row))
  4790.           goto edit;
  4791.  
  4792.         if(entry_is_clickable(as.top_ent+as.cur_row)){
  4793.         DL_CACHE_S *dlc_to_flush;
  4794.  
  4795.         start_disp = as.cur_row;  /* will redraw from here down */
  4796.         dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
  4797.         if(dlc_to_flush->type == DlcClickHere){
  4798.  
  4799.             /*
  4800.              * open this addrbook and fill in display list
  4801.              */
  4802.  
  4803.             /* flush the CLICKHERE line from dlc cache */
  4804.             flush_dlc_from_cache(dlc_to_flush);
  4805.             init_abook(pab, Open);
  4806.             if(!are_selecting && pab->access == ReadOnly)
  4807.               readonly_warning(NO_DING, pab->nickname);
  4808.         }
  4809.         else{
  4810.  
  4811.             /*
  4812.              * expand this distribution list
  4813.              */
  4814.  
  4815.             /*
  4816.              * Mark this list expanded, then flush the
  4817.              * ListCLICKHERE line from dlc cache.  When we get the
  4818.              * line again it will notice the expanded flag and change
  4819.              * the type to DlcListEnt (if any entries).  The check
  4820.              * in the if should be unnecessary, since we won't get
  4821.              * here if it is turned on.
  4822.              */
  4823.             if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
  4824.               exp_set_expanded(pab->address_book->exp,
  4825.             (a_c_arg_t)dlc_to_flush->dlcelnum);
  4826.  
  4827.             flush_dlc_from_cache(dlc_to_flush);
  4828.             dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
  4829.             if(dlc_to_flush->type == DlcListEmpty){
  4830.             as.cur_row--;
  4831.             start_disp--;
  4832.             if(as.cur_row < 0){
  4833.                 as.top_ent--;
  4834.                 as.cur_row = 0;
  4835.                 start_disp  = 0;
  4836.             }
  4837.             }
  4838.         }
  4839.  
  4840.         ps->mangled_body = 1;
  4841.         }
  4842.         else if(are_selecting){
  4843.               /* Select an entry to mail to or a nickname to add to */
  4844.           if(!any_addrs_avail(as.top_ent+as.cur_row)){
  4845.               q_status_message(SM_ORDER | SM_DING, 0, 4,
  4846.        "No entries in address book. Use ExitSelect to leave address book");
  4847.               break;
  4848.           }
  4849.  
  4850.           if(as.checkboxes || is_addr(as.top_ent+as.cur_row)){
  4851.           BuildTo        bldto;
  4852.           char          *to    = NULL;
  4853.           char          *error = NULL;
  4854.           AdrBk_Entry   *abe;
  4855.  
  4856.           dl = dlist(as.top_ent+as.cur_row);
  4857.  
  4858.           if(selecting_one_nick
  4859.              || (selecting_mult_nicks && return_array
  4860.              && !as.checkboxes)){
  4861.               char nickbuf[MAX_NICKNAME + 1];
  4862.  
  4863.               strncpy(nickbuf,
  4864.               ae(as.top_ent+as.cur_row)->nickname,
  4865.               MAX_NICKNAME);
  4866.               if(selecting_one_nick)
  4867.             return(cpystr(nickbuf));
  4868.               else{
  4869.               *return_array = (char **)fs_get(2 * sizeof(char *));
  4870.               memset((void *)*return_array, 0, 2 * sizeof(char *));
  4871.               (*return_array)[0] = cpystr(nickbuf);
  4872.               return(NULL);
  4873.               }
  4874.           }
  4875.           else if(as.checkboxes && checkedn <= 0){
  4876.               q_status_message(SM_ORDER, 0, 1,
  4877.             "Use \"X\" to mark addresses or lists");
  4878.               break;
  4879.           }
  4880.           else if(as.checkboxes){
  4881.               size_t incr = 100, avail, alloced;
  4882.               int ind;
  4883.  
  4884.               /*
  4885.                * Have to run through all of the checked entries
  4886.                * in all of the address books.
  4887.                * Put the nicknames together into one long
  4888.                * string with comma separators and let 
  4889.                * our_build_address handle the parsing.
  4890.                */
  4891.               if(selecting_mult_nicks && return_array){
  4892.               *return_array = (char **)fs_get((checkedn+1)
  4893.                             * sizeof(char *));
  4894.               memset((void *)*return_array, 0, (checkedn+1)
  4895.                                * sizeof(char *));
  4896.               ind = 0;
  4897.               }
  4898.               else{
  4899.               to = (char *)fs_get(incr);
  4900.               *to = '\0';
  4901.               avail = incr;
  4902.               alloced = incr;
  4903.               }
  4904.  
  4905.               for(i = 0; i < as.n_addrbk; i++){
  4906.               EXPANDED_S *next_one;
  4907.               adrbk_cntr_t num;
  4908.               AddrScrn_Disp fake_dl;
  4909.               char *a_string;
  4910.  
  4911.               pab = &as.adrbks[i];
  4912.               if(pab->address_book)
  4913.                 next_one = pab->address_book->checks;
  4914.               else
  4915.                 continue;
  4916.  
  4917.               while((num = entry_get_next(&next_one)) != NO_NEXT){
  4918.                   abe = adrbk_get_ae(pab->address_book,
  4919.                          (a_c_arg_t)num, Normal);
  4920.                   /*
  4921.                    * Since we're picking up address book entries
  4922.                    * directly from the address books and have
  4923.                    * no knowledge of the display lines they came
  4924.                    * from, we don't know the dl's that go with
  4925.                    * them.  We need to pass a dl to abe_to_nick
  4926.                    * but it really is only going to use the
  4927.                    * type in this case.
  4928.                    */
  4929.                   dl = &fake_dl;
  4930.                   dl->type = (abe->tag == Single) ? Simple
  4931.                                   : ListHead;
  4932.                   a_string = abe_to_nick_or_addr_string(abe,
  4933.                              dl, pab->address_book);
  4934.                   if(selecting_mult_nicks && return_array){
  4935.                   (*return_array)[ind++] = a_string;
  4936.                   }
  4937.                   else{
  4938.                   while(abe && avail
  4939.                       < (size_t)strlen(a_string)+1){
  4940.                       alloced += incr;
  4941.                       avail   += incr;
  4942.                       fs_resize((void **)&to, alloced);
  4943.                   }
  4944.  
  4945.                   if(!*to)
  4946.                     strcpy(to, a_string);
  4947.                   else{
  4948.                       strcat(to, ",");
  4949.                       strcat(to, a_string);
  4950.                   }
  4951.  
  4952.                   avail -= (strlen(a_string) + 1);
  4953.                   }
  4954.               }
  4955.               }
  4956.  
  4957.               /*
  4958.                * Return the nickname list for lcc so that the
  4959.                * correct fullname can make it to the To line.
  4960.                * If we expand it ahead of time, the list name
  4961.                * and first user's fullname will get mushed together.
  4962.                * If an entry doesn't have a nickname then we're
  4963.                * out of luck as far as getting the right entry
  4964.                * in the To line goes.
  4965.                */
  4966.               if(selecting_mult_nicks){
  4967.               if(return_array)
  4968.                 return(NULL); /* return value is ignored */
  4969.               else
  4970.                 return(to);
  4971.               }
  4972.  
  4973.               bldto.type    = Str;
  4974.               bldto.arg.str = to;
  4975.           }
  4976.           else{
  4977.               /* Select an address, but not using checkboxes */
  4978.               if(selecting_mult_nicks){
  4979.             if(dl->type != ListHead && style == SelectAddrLccCom){
  4980.                 q_status_message(SM_ORDER, 0, 4,
  4981.       "You may only select lists for lcc, use bcc for other addresses");
  4982.                 break;
  4983.             }
  4984.             else{
  4985.                 /*
  4986.                  * Even though we're supposedly selecting
  4987.                  * nicknames, we have a special case here to
  4988.                  * select a single member of a distribution
  4989.                  * list.  This happens with style SelectNicksCom
  4990.                  * which is the regular ^T entry from the
  4991.                  * composer, and it allows somebody to mail to
  4992.                  * a single member of a distribution list.
  4993.                  */
  4994.                 abe = ae(as.top_ent+as.cur_row);
  4995.                 return(abe_to_nick_or_addr_string(abe, dl,
  4996.                                 pab->address_book));
  4997.                 }
  4998.             }
  4999.               else{
  5000.               if(dl->type == ListEnt){
  5001.                   bldto.type    = Str;
  5002.                   bldto.arg.str =
  5003.                       listmem_from_dl(pab->address_book, dl);
  5004.               }
  5005.               else if(dl->type == ListHead && no_fullname){
  5006.                   q_status_message(SM_ORDER, 0, 4,
  5007.       "You may not select a list, select a single address instead");
  5008.                   break;
  5009.               }
  5010.               else{
  5011.                   bldto.type    = Abe;
  5012.                   bldto.arg.abe = ae(as.top_ent+as.cur_row);
  5013.               }
  5014.               }
  5015.           }
  5016.               
  5017.           if(no_fullname){
  5018.               if(bldto.type == Str){
  5019.               char *q, *p, *t;
  5020.  
  5021.               q = cpystr(bldto.arg.str);
  5022.               /*
  5023.                * make an attempt to remove full name if it's there
  5024.                */
  5025.               if((p = strrindex(q, '>')) == NULL)
  5026.                 addr = q;
  5027.               else{
  5028.                   *p = '\0';
  5029.                   if((t = strrindex(q, '<')) != NULL){
  5030.                   addr = cpystr(t+1);
  5031.                   fs_give((void **)&q);
  5032.                   removing_leading_white_space(addr);
  5033.                   removing_trailing_white_space(addr);
  5034.                   }
  5035.                   else{
  5036.                   *p = '>';
  5037.                   addr = q;
  5038.                   }
  5039.               }
  5040.               }
  5041.               else
  5042.                 addr = cpystr((bldto.arg.abe &&
  5043.                         bldto.arg.abe->addr.addr)
  5044.                     ? bldto.arg.abe->addr.addr : "");
  5045.           }
  5046.           else{
  5047.               (void)our_build_address(bldto, &addr, &error, NULL, 1, 0);
  5048.               /* Have to rfc1522_decode the addr */
  5049.               if(addr){
  5050.               char *p, *dummy = NULL;
  5051.  
  5052.               p = (char *)fs_get((strlen(addr) + 1) * sizeof(char));
  5053.               if(rfc1522_decode((unsigned char *)p, addr, &dummy)
  5054.                               == (unsigned char *)p){
  5055.                   fs_give((void **)&addr);
  5056.                   addr = p;
  5057.               }
  5058.               else
  5059.                 fs_give((void **)&p);
  5060.               
  5061.               if(dummy)
  5062.                 fs_give((void **)&dummy);
  5063.               }
  5064.           }
  5065.  
  5066.           if(to)
  5067.             fs_give((void **)&to);
  5068.  
  5069.           if(error){
  5070.               q_status_message1(SM_ORDER, 3, 4, "%s", error);
  5071.               fs_give((void **)&error);
  5072.           }
  5073.  
  5074.           return(addr);  /* Caller frees this */
  5075.           }
  5076.           else{
  5077.               q_status_message1(SM_ORDER, 3, 4, "No %s selected",
  5078.               selecting_nick ? "nickname" : "address");
  5079.               break;
  5080.           }
  5081.         }
  5082.  
  5083.         break;
  5084.  
  5085.  
  5086.             /*----- Edit existing or Add new ---------*/
  5087.       case 'a':
  5088.           case PF10:
  5089.           case 'v':
  5090.         if(are_selecting)
  5091.           goto bleep;
  5092. edit:
  5093.         if((c == 'v' || c == PF4 || c == KEY_MOUSE)
  5094.           && !any_addrs_avail(as.top_ent+as.cur_row)){
  5095.                 q_status_message(SM_ORDER, 0, 4, "No entries to view");
  5096.                 break;
  5097.             }
  5098.  
  5099.         if(((dl=dlist(as.top_ent+as.cur_row))->type == ClickHere)
  5100.           || ((c == 'v' || c == PF4 || c == KEY_MOUSE)
  5101.           && dl->type == ListClickHere)){
  5102.         clickable_warning(as.top_ent+as.cur_row);
  5103.         break;
  5104.         }
  5105.  
  5106.         if(rdonly && (c == 'a' || c == PF10)){
  5107.         readonly_warning(NO_DING, NULL);
  5108.                 break;
  5109.         }
  5110.  
  5111.         if(empty && (c == 'v' || c == PF4 || c == KEY_MOUSE)){
  5112.         empty_warning(as.top_ent+as.cur_row);
  5113.                 break;
  5114.         }
  5115.  
  5116.         warped = 0;
  5117.         if(c == 'a' || c == PF10) 
  5118.           edit_entry(pab->address_book, (AdrBk_Entry *)NULL, NO_NEXT,
  5119.                               NotSet, 0, &warped);
  5120.         else{
  5121.         if(is_addr(as.cur_row+as.top_ent)){
  5122.             AdrBk_Entry *abe, *abe_copy;
  5123.             a_c_arg_t entry;
  5124.  
  5125.             entry = dl->elnum;
  5126.             abe = adrbk_get_ae(pab->address_book, entry, Normal);
  5127.             abe_copy = adrbk_newentry();
  5128.             abe_copy->nickname
  5129.                = abe->nickname ? cpystr(abe->nickname) : abe->nickname;
  5130.             abe_copy->fullname
  5131.                = abe->fullname ? cpystr(abe->fullname) : abe->fullname;
  5132.             abe_copy->fcc
  5133.                = abe->fcc ? cpystr(abe->fcc) : abe->fcc;
  5134.             abe_copy->extra
  5135.                = abe->extra ? cpystr(abe->extra) : abe->extra;
  5136.             abe_copy->tag = abe->tag;
  5137.             if(abe_copy->tag == Single)
  5138.               abe_copy->addr.addr = cpystr(abe->addr.addr);
  5139.             else{
  5140.             char **p;
  5141.             size_t size;
  5142.             int j;
  5143.  
  5144.             /* copy list */
  5145.             /* count up size of list */
  5146.             for(p = abe->addr.list; p != NULL && *p != NULL; p++)
  5147.               ;/* do nothing */
  5148.             
  5149.             size = p - abe->addr.list;
  5150.             abe_copy->addr.list = (char **)fs_get((size+1)
  5151.                                 * sizeof(char *));
  5152.             for(j = 0; j < size; j++)
  5153.               abe_copy->addr.list[j] = cpystr(abe->addr.list[j]);
  5154.  
  5155.             abe_copy->addr.list[size] = NULL;
  5156.             }
  5157.  
  5158.             edit_entry(pab->address_book, abe_copy, entry,
  5159.                    abe->tag, rdonly, &warped);
  5160.             free_ae(pab->address_book, &abe_copy);
  5161.         }
  5162.         else{
  5163.             q_status_message(SM_ORDER, 0, 3,
  5164.                      "Current line is not editable");
  5165.             break;
  5166.         }
  5167.         }
  5168.  
  5169.         /*
  5170.          * Warped means we got plopped down somewhere in the display
  5171.          * list so that we don't know where we are relative to where
  5172.          * we were before we warped.  The current line number will
  5173.          * be zero, since that is what the warp would have set.
  5174.          */
  5175.         {long old_l_p_p, old_top_ent, old_cur_row;
  5176.  
  5177.         if(warped){
  5178.             as.top_ent = first_line(0L - as.l_p_page/2L);
  5179.             as.cur_row = 0L - as.top_ent;
  5180.         }
  5181.         else{
  5182.             /*
  5183.              * If we didn't warp, that means we didn't change at all,
  5184.              * so keep old screen.
  5185.              */
  5186.             old_l_p_p   = as.l_p_page;
  5187.             old_top_ent = as.top_ent;
  5188.             old_cur_row = as.cur_row;
  5189.         }
  5190.  
  5191.         /* Window size may have changed while in pico. */
  5192.         ab_resize();
  5193.  
  5194.         /* fix up what ab_resize messed up */
  5195.         if(!warped && old_l_p_p == as.l_p_page){
  5196.             as.top_ent     = old_top_ent;
  5197.             as.cur_row     = old_cur_row;
  5198.             as.old_cur_row = old_cur_row;
  5199.         }
  5200.         }
  5201.  
  5202.         ps->mangled_screen = 1;
  5203.         break;
  5204.  
  5205.  
  5206.             /*----------------------- Move Up ---------------------*/
  5207.           case PF5:
  5208.       case 'p':
  5209.           case ctrl('P'):
  5210.           case KEY_UP:
  5211.         if(any_addrs_avail(as.top_ent+as.cur_row)){
  5212.  
  5213.         r = prev_selectable_line(as.cur_row+as.top_ent, &new_line);
  5214.         if(r == 0){
  5215.             q_status_message(SM_INFO, 0, 1, "Already on first line.");
  5216.             break;
  5217.         }
  5218.  
  5219.         i = ((c == ctrl('P') || c == KEY_UP)
  5220.              && dlist(as.top_ent - 1L)->type != Beginning)
  5221.               ? min(HS_MARGIN(ps), as.cur_row) : 0;
  5222.                 as.cur_row = new_line - as.top_ent;
  5223.                 if(as.cur_row - i < 0){
  5224.             if(c == ctrl('P') || c == KEY_UP){
  5225.             /*-- Past top of page --*/
  5226.             as.top_ent += (as.cur_row - i);
  5227.             as.cur_row  = i;
  5228.             }
  5229.             else{
  5230.             new_top_ent = first_line(as.top_ent - as.l_p_page);
  5231.             as.cur_row += (as.top_ent - new_top_ent);
  5232.             as.top_ent  = new_top_ent;
  5233.             }
  5234.  
  5235.             start_disp       = 0;
  5236.             ps->mangled_body = 1;
  5237.                 }
  5238.         else
  5239.                   current_changed_flag++;
  5240.         }
  5241.         else
  5242.           empty_warning(as.top_ent+as.cur_row);
  5243.  
  5244.         break;
  5245.  
  5246.  
  5247.             /*------------------- Move Down -------------------*/
  5248.           case PF6:
  5249.           case '\t':
  5250.           case 'n':
  5251.           case ctrl('N'):
  5252.           case KEY_DOWN:
  5253.         if(any_addrs_avail(as.top_ent+as.cur_row)){
  5254.  
  5255.         r = next_selectable_line(as.cur_row+as.top_ent, &new_line);
  5256.         if(r == 0){
  5257.             q_status_message(SM_INFO, 0, 1, "Already on last line.");
  5258.             break;
  5259.         }
  5260.  
  5261.         /* adjust for scrolling margin */
  5262.         i = (c == ctrl('N') || c == KEY_DOWN) ? HS_MARGIN(ps) : 0;
  5263.                 as.cur_row = new_line - as.top_ent;
  5264.         if(as.cur_row >= as.l_p_page - i){
  5265.             if(c == ctrl('N') || c == KEY_DOWN){
  5266.             /*-- Past bottom of page --*/
  5267.             as.top_ent += (as.cur_row - (as.l_p_page - i) + 1);
  5268.             as.cur_row  = (as.l_p_page - i) - 1;
  5269.             }
  5270.             else{
  5271.             /*-- Changed pages --*/
  5272.             as.top_ent += as.l_p_page;
  5273.             as.cur_row -= as.l_p_page;
  5274.             }
  5275.  
  5276.             start_disp       = 0;
  5277.             ps->mangled_body = 1;
  5278.                 }
  5279.         else
  5280.                   current_changed_flag++;
  5281.         }
  5282.         else
  5283.           empty_warning(as.top_ent+as.cur_row);
  5284.  
  5285.         break;
  5286.  
  5287.  
  5288. #ifdef MOUSE        
  5289.       case KEY_MOUSE:
  5290.         {
  5291.         MOUSEPRESS mp;
  5292.         
  5293.         /*
  5294.          * Get the mouse down.  Convert to content row number.
  5295.          * If the row is selectable, do the single or double click
  5296.          * operation.
  5297.          */
  5298.         mouse_get_last(NULL, &mp);
  5299.         mp.row -= HEADER_ROWS(ps_global);
  5300.         if(line_is_selectable(as.top_ent + mp.row)){
  5301.             if(mp.doubleclick){
  5302.             if(are_selecting && as.checkboxes
  5303.                && !entry_is_clickable(as.top_ent+as.cur_row))
  5304.               goto togglex;
  5305.             else if(!are_selecting
  5306.                && !entry_is_clickable(as.top_ent+as.cur_row))
  5307.               goto edit;
  5308.             else
  5309.               goto select;
  5310.             }
  5311.  
  5312.             as.cur_row = mp.row;
  5313.             current_changed_flag++;
  5314.         }
  5315.         }
  5316.         break;
  5317. #endif        
  5318.  
  5319.  
  5320.             /*------------- Page Up ----------------*/
  5321.       case '-':
  5322.           case ctrl('Y'): 
  5323.       case PF7:
  5324.       case KEY_PGUP:
  5325.             /*------------- Page Down --------------*/
  5326.           case SPACE:
  5327.           case ctrl('V'): 
  5328.           case '+':            
  5329.       case PF8:
  5330.       case KEY_PGDN:
  5331.         /* if Up */
  5332.         if(c == '-' || c == ctrl('Y') || c == PF7 || c == KEY_PGUP){
  5333.         /* find first line on prev page */
  5334.         new_top_ent = first_line(as.top_ent - as.l_p_page);
  5335.         if(new_top_ent == NO_LINE)
  5336.             break;
  5337.  
  5338.         /* find first selectable line */
  5339.         fl = first_selectable_line(new_top_ent);
  5340.         if(fl == NO_LINE)
  5341.             break;
  5342.  
  5343.         if(as.top_ent == new_top_ent && as.cur_row == (fl-as.top_ent)){
  5344.             q_status_message(SM_INFO, 0, 1, "Already on first page.");
  5345.             break;
  5346.         }
  5347.  
  5348.         if(as.top_ent == new_top_ent)
  5349.                     current_changed_flag++;
  5350.         else
  5351.             as.top_ent = new_top_ent;
  5352.         }
  5353.         /* else Down */
  5354.         else{
  5355.         /* find first selectable line on next page */
  5356.         fl = first_selectable_line(as.top_ent + as.l_p_page);
  5357.         if(fl == NO_LINE)
  5358.             break;
  5359.  
  5360.         /* if there is another page, scroll */
  5361.         if(fl - as.top_ent >= as.l_p_page){
  5362.             new_top_ent = as.top_ent + as.l_p_page;
  5363.         }
  5364.         /* on last page already */
  5365.         else{
  5366.             new_top_ent = as.top_ent;
  5367.             if(as.cur_row == (fl - as.top_ent)){ /* no change */
  5368.             q_status_message(SM_INFO,0,1,"Already on last page.");
  5369.             break;
  5370.             }
  5371.         }
  5372.  
  5373.         if(as.top_ent == new_top_ent)
  5374.                     current_changed_flag++;
  5375.         else
  5376.             as.top_ent = new_top_ent;
  5377.         }
  5378.  
  5379.         as.cur_row = fl - as.top_ent;
  5380.         if(!current_changed_flag){
  5381.         ps->mangled_body = 1;
  5382.         start_disp  = 0;
  5383.         }
  5384.  
  5385.         break;
  5386.  
  5387.  
  5388.         /*------------- Delete item from addrbook ---------*/
  5389.       case PF9:
  5390.       case 'd': 
  5391.         if(as.checkboxes && c == PF9)
  5392.           goto togglex;
  5393.         
  5394.         if(!as.checkboxes && c == PF9 && listmode_ok)
  5395.           goto listmodeon;
  5396.  
  5397.         if(are_selecting)
  5398.           goto bleep;
  5399.  
  5400.         if(!any_addrs_avail(as.top_ent+as.cur_row)){
  5401.                 q_status_message(SM_ORDER, 0, 4, "No entries to delete");
  5402.                 break;
  5403.         }
  5404.  
  5405.         if(entry_is_clickable(as.top_ent+as.cur_row)){
  5406.         clickable_warning(as.top_ent+as.cur_row);
  5407.         break;
  5408.         }
  5409.  
  5410.         if(rdonly){
  5411.         readonly_warning(NO_DING, NULL);
  5412.         break;
  5413.         }
  5414.  
  5415.         if(empty){
  5416.         empty_warning(as.top_ent+as.cur_row);
  5417.                 break;
  5418.         }
  5419.  
  5420.         warped = 0;
  5421.             did_delete_flag = addr_book_delete(pab->address_book,
  5422.                            command_line,
  5423.                                                as.cur_row+as.top_ent,
  5424.                                                &warped);
  5425.         ps->mangled_footer = 1;
  5426.         if(did_delete_flag){
  5427.         if(warped){
  5428.             as.top_ent = first_line(0L - as.l_p_page/2L);
  5429.             as.cur_row = 0L - as.top_ent;
  5430.             start_disp = 0;
  5431.         }
  5432.         else{
  5433.             /*
  5434.              * In case the line we're now at is not a selectable
  5435.              * field.
  5436.              */
  5437.             new_line = first_selectable_line(as.cur_row+as.top_ent);
  5438.             if(new_line != NO_LINE
  5439.                && new_line != as.cur_row+as.top_ent){
  5440.             as.cur_row = new_line - as.top_ent;
  5441.             if(as.cur_row < 0){
  5442.                 as.top_ent -= as.l_p_page;
  5443.                 as.cur_row += as.l_p_page;
  5444.             }
  5445.             else if(as.cur_row >= as.l_p_page){
  5446.                 as.top_ent += as.l_p_page;
  5447.                 as.cur_row -= as.l_p_page;
  5448.             }
  5449.             }
  5450.  
  5451.             start_disp = min(as.cur_row, as.old_cur_row);
  5452.         }
  5453.  
  5454.             ps->mangled_body = 1;
  5455.         }
  5456.  
  5457.             break;
  5458.  
  5459.  
  5460.         /*------------- Toggle checkbox ---------*/
  5461.       case 'x': 
  5462.       togglex:
  5463.             if(!are_selecting)
  5464.               goto export;
  5465.  
  5466.         if(!as.checkboxes)
  5467.           goto bleep;
  5468.  
  5469.         if(!any_addrs_avail(as.top_ent+as.cur_row)){
  5470.                 q_status_message(SM_ORDER, 0, 4, "No entries to select");
  5471.                 break;
  5472.         }
  5473.  
  5474.         if(entry_is_clickable(as.top_ent+as.cur_row)){
  5475.         clickable_warning(as.top_ent+as.cur_row);
  5476.         break;
  5477.         }
  5478.  
  5479.         if(empty){
  5480.         empty_warning(as.top_ent+as.cur_row);
  5481.                 break;
  5482.         }
  5483.  
  5484.         if(is_addr(as.top_ent+as.cur_row)){
  5485.         dl = dlist(as.top_ent+as.cur_row);
  5486.  
  5487.         if(style == SelectAddrLccCom && dl->type != ListHead)
  5488.           q_status_message(SM_ORDER, 0, 4,
  5489.       "You may only select lists for lcc, use bcc for personal entries");
  5490.         else if(dl->type == ListHead || dl->type == Simple){
  5491.                     current_changed_flag++;
  5492.             if(entry_is_checked(pab->address_book->checks,
  5493.                         (a_c_arg_t)dl->elnum)){
  5494.             entry_unset_checked(pab->address_book->checks,
  5495.                         (a_c_arg_t)dl->elnum);
  5496.             checkedn--;
  5497.             }
  5498.             else{
  5499.             entry_set_checked(pab->address_book->checks,
  5500.                         (a_c_arg_t)dl->elnum);
  5501.             checkedn++;
  5502.             }
  5503.         }
  5504.         else
  5505.           q_status_message(SM_ORDER, 0, 4,
  5506.       "You may not select list members, only whole lists or personal entries");
  5507.         }
  5508.         else
  5509.               q_status_message(SM_ORDER, 0, 4,
  5510.           "You may only select addresses or lists");
  5511.  
  5512.             break;
  5513.  
  5514.  
  5515.         /*---------- Turn on ListMode -----------*/
  5516.       listmodeon:
  5517.         as.checkboxes = 1;
  5518.         for(i = 0; i < as.n_addrbk; i++){
  5519.         pab = &as.adrbks[i];
  5520.         init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
  5521.         }
  5522.  
  5523.         (void)calculate_field_widths();
  5524.         ps->mangled_footer = 1;
  5525.         ps->mangled_body = 1;
  5526.         start_disp  = 0;
  5527.             q_status_message(SM_ORDER, 0, 4,
  5528.           "Use \"X\" to select addresses or lists");
  5529.             break;
  5530.  
  5531.  
  5532.             /*--------- Compose -----------*/
  5533.           case PF11:
  5534.       case 'c':
  5535.         if(are_selecting)
  5536.           goto bleep;
  5537.  
  5538.         ab_compose_to_addr(as.top_ent+as.cur_row);
  5539.         /*
  5540.          * Window size may have changed in composer.
  5541.          * Pine_send will have reset the window size correctly,
  5542.          * but we still have to reset our data structures.
  5543.          */
  5544.         ab_resize();
  5545.         ps->mangled_screen = 1;
  5546.             break;
  5547.  
  5548.  
  5549.             /*----------- Where is (search) ----------------*/
  5550.       case PF12:
  5551.       case 'w':
  5552.       case ctrl('W'):
  5553.         warped = 0;
  5554.         new_top_ent = ab_whereis(&warped, command_line);
  5555.  
  5556.         if(new_top_ent != NO_LINE){
  5557.         if(warped || new_top_ent != as.top_ent){
  5558.             as.top_ent     = new_top_ent;
  5559.             start_disp     = 0;
  5560.             ps->mangled_body = 1;
  5561.         }
  5562.         else
  5563.             current_changed_flag++;
  5564.         }
  5565.  
  5566.         ps->mangled_footer = 1;
  5567.         break;
  5568.  
  5569.  
  5570.             /*--------- QUIT pine -----------*/
  5571.           case OPF3:
  5572.       case 'q':
  5573.             if(are_selecting)
  5574.               goto bleep;
  5575.  
  5576.             ps_global->next_screen = quit_screen;
  5577.         quit = 1;
  5578.             break;
  5579.  
  5580.  
  5581.             /*--------- Folders -----------*/
  5582.           case OPF5:
  5583.       case 'l':
  5584.         if(!as.checkboxes && c == 'l' && listmode_ok)
  5585.           goto listmodeon;
  5586.  
  5587.             if(are_selecting)
  5588.               goto bleep;
  5589.  
  5590.             ps_global->next_screen = folder_screen;
  5591.         quit = 1;
  5592.             break;
  5593.  
  5594.  
  5595.             /*---------- Open specific new folder ----------*/
  5596.           case OPF6:
  5597.       case 'g':
  5598.             if(are_selecting || ps_global->nr_mode)
  5599.               goto bleep;
  5600.  
  5601.         ab_goto_folder(command_line);
  5602.         quit = 1;
  5603.             break;
  5604.  
  5605.  
  5606.             /*--------- Index -----------*/
  5607.           case OPF7:
  5608.       case 'i':
  5609.             if(are_selecting)
  5610.               goto bleep;
  5611.  
  5612.             ps_global->next_screen = mail_index_screen;
  5613.         quit = 1;
  5614.             break;
  5615.  
  5616.  
  5617.         /*----------------- Print --------------------*/
  5618.       case OPF9: 
  5619.       case 'y':
  5620.         ab_print();
  5621.         ps->mangled_screen = 1;
  5622.             break;
  5623.  
  5624.  
  5625.         /*------ Take from one to another -------------*/
  5626.       case OPF10: 
  5627.       case 't':
  5628.         {
  5629.         AdrBk_Entry   *abe;
  5630.         adrbk_cntr_t   new_entry_num;
  5631.         AddrScrn_Disp  save_dl;
  5632.         DL_CACHE_S     dlc_restart;
  5633.         char          *save_nick = NULL,
  5634.               *save_addr = NULL;
  5635.         int            found_old_one = 0;
  5636.  
  5637.         if(are_selecting)
  5638.           goto bleep;
  5639.  
  5640.         if(!any_addrs_avail(as.top_ent+as.cur_row)){
  5641.                 q_status_message(SM_ORDER, 0, 4, "No entries to take");
  5642.                 break;
  5643.         }
  5644.  
  5645.         if(entry_is_clickable(as.top_ent+as.cur_row)){
  5646.         clickable_warning(as.top_ent+as.cur_row);
  5647.         break;
  5648.         }
  5649.  
  5650.         if(empty){
  5651.         empty_warning(as.top_ent+as.cur_row);
  5652.                 break;
  5653.         }
  5654.  
  5655.         /*
  5656.          * Save some state information so that we can probably
  5657.          * redraw the screen halfway intelligently.
  5658.          */
  5659.         dl = dlist(as.top_ent+as.cur_row);
  5660.         save_dl.type = dl->type;
  5661.         abe = ae(as.top_ent+as.cur_row);
  5662.         if(abe && abe->nickname)
  5663.           save_nick = cpystr(abe->nickname);
  5664.  
  5665.         switch(save_dl.type){
  5666.           case ListEnt:
  5667.         save_dl.l_offset = dl->l_offset;
  5668.         if(abe && abe->addr.list &&
  5669.            listmem_count_from_abe(abe) > save_dl.l_offset &&
  5670.            abe->addr.list[save_dl.l_offset])
  5671.           save_addr = cpystr(abe->addr.list[save_dl.l_offset]);
  5672.  
  5673.         /* fall through */
  5674.  
  5675.           case Simple:
  5676.           case ListHead:
  5677.         save_dl.elnum = dl->elnum;
  5678.         break;
  5679.         }
  5680.  
  5681.             internal_take(pab->address_book, as.cur_row+as.top_ent);
  5682.         
  5683.         /*
  5684.          * We want to try to position the cursor at the same place it
  5685.          * was before the take.  Depending on what happened during the
  5686.          * take, the entry we were looking at before could now be at
  5687.          * the same elnum in the addrbook, or at + or -1 from that
  5688.          * spot.  Look for that old nickname.  If we can't find it,
  5689.          * that means we did a take to the same nickname we were taking
  5690.          * from and changed something so that it sorts differently now.
  5691.          * That should be rare.  In that case, just go with elnum.
  5692.          * In any case, we don't have enough information to redraw the
  5693.          * screen in the same place as it was before, so we center the
  5694.          * place we were before in the middle of the screen.
  5695.          */
  5696.         if(save_nick &&
  5697.            (abe = /* that is supposed to be a single '=' */
  5698.              adrbk_get_ae(pab->address_book,
  5699.              (a_c_arg_t)save_dl.elnum, Normal))
  5700.              && abe->nickname
  5701.              && strcmp(save_nick, abe->nickname) == 0){
  5702.         new_entry_num = save_dl.elnum;
  5703.         found_old_one++;
  5704.         }
  5705.         else if(save_nick &&
  5706.             (abe =
  5707.                adrbk_get_ae(pab->address_book,
  5708.                (a_c_arg_t)(save_dl.elnum+1), Normal))
  5709.                && abe->nickname
  5710.                && strcmp(save_nick, abe->nickname) == 0){
  5711.         new_entry_num = save_dl.elnum + 1;
  5712.         found_old_one++;
  5713.         }
  5714.         else if(save_nick &&
  5715.             (abe =
  5716.                adrbk_get_ae(pab->address_book,
  5717.                (a_c_arg_t)(save_dl.elnum-1), Normal))
  5718.                && abe->nickname
  5719.                && strcmp(save_nick, abe->nickname) == 0){
  5720.         new_entry_num = save_dl.elnum - 1;
  5721.         found_old_one++;
  5722.         }
  5723.         else{
  5724.         abe = adrbk_get_ae(pab->address_book,
  5725.             (a_c_arg_t)save_dl.elnum, Normal);
  5726.         new_entry_num = save_dl.elnum;
  5727.         }
  5728.  
  5729.         /*
  5730.          * Build a dlc and warp to it.
  5731.          */
  5732.         if(abe && abe->tag == Single){
  5733.         dlc_restart.adrbk_num = as.cur;
  5734.         dlc_restart.dlcelnum  = new_entry_num;
  5735.         dlc_restart.type      = DlcSimple;
  5736.         warp_to_dlc(&dlc_restart, 0L);
  5737.         /* put current entry in middle of screen */
  5738.         as.top_ent = first_line(0L - (long)as.l_p_page/2L);
  5739.         as.cur_row = 0L - as.top_ent;
  5740.         }
  5741.         else if(abe && abe->tag == List){
  5742.  
  5743.         dlc_restart.adrbk_num = as.cur;
  5744.         dlc_restart.dlcelnum  = new_entry_num;
  5745.  
  5746.         if(found_old_one &&
  5747.            save_dl.type == ListEnt &&
  5748.            save_addr &&
  5749.            listmem_count_from_abe(abe) > save_dl.l_offset &&
  5750.            abe->addr.list &&
  5751.            abe->addr.list[save_dl.l_offset] &&
  5752.            strcmp(abe->addr.list[save_dl.l_offset], save_addr)==0){
  5753.  
  5754.             dlc_restart.type      = DlcListEnt;
  5755.             dlc_restart.dlcoffset = save_dl.l_offset;
  5756.         }
  5757.         else
  5758.           dlc_restart.type      = DlcListHead;
  5759.  
  5760.         warp_to_dlc(&dlc_restart, 0L);
  5761.         /* put current entry in middle of screen */
  5762.         as.top_ent = first_line(0L - (long)as.l_p_page/2L);
  5763.         as.cur_row = 0L - as.top_ent;
  5764.         }
  5765.         else{
  5766.         /* No abe to go to, shouldn't happen. */
  5767.         warp_to_beginning();
  5768.         as.cur         = 0;
  5769.         as.top_ent     = 0L;
  5770.         new_ent        = first_selectable_line(0L);
  5771.         if(new_ent == NO_LINE)
  5772.           as.cur_row = 0L;
  5773.         else
  5774.           as.cur_row = new_ent;
  5775.  
  5776.         if(as.cur_row >= as.l_p_page)
  5777.           as.top_ent += (as.cur_row - as.l_p_page + 1);
  5778.         }
  5779.  
  5780.         ps->mangled_screen = 1;
  5781.         if(save_nick)
  5782.           fs_give((void **)&save_nick);
  5783.  
  5784.         if(save_addr)
  5785.           fs_give((void **)&save_addr);
  5786.         }
  5787.  
  5788.             break;
  5789.  
  5790.  
  5791.       case OPF11: 
  5792.       export:
  5793.         if(!any_addrs_avail(as.top_ent+as.cur_row)){
  5794.                 q_status_message(SM_ORDER, 0, 4, "No entries to export");
  5795.                 break;
  5796.         }
  5797.  
  5798.         if(entry_is_clickable(as.top_ent+as.cur_row)){
  5799.         clickable_warning(as.top_ent+as.cur_row);
  5800.         break;
  5801.         }
  5802.  
  5803.         if(empty){
  5804.         empty_warning(as.top_ent+as.cur_row);
  5805.                 break;
  5806.         }
  5807.  
  5808.         if(!is_addr(as.top_ent+as.cur_row)){
  5809.                 q_status_message(SM_ORDER, 0, 4, "Nothing to export");
  5810.                 break;
  5811.         }
  5812.  
  5813.         ab_export(as.top_ent+as.cur_row, command_line);
  5814.             ps->mangled_footer = 1;
  5815.             break;
  5816.  
  5817.  
  5818.       case OPF12: 
  5819.       case 'f': 
  5820.         if(!any_addrs_avail(as.top_ent+as.cur_row)){
  5821.                 q_status_message(SM_ORDER, 0, 4, "No entries to forward");
  5822.                 break;
  5823.         }
  5824.  
  5825.         if(entry_is_clickable(as.top_ent+as.cur_row)){
  5826.         clickable_warning(as.top_ent+as.cur_row);
  5827.         break;
  5828.         }
  5829.  
  5830.         if(empty){
  5831.         empty_warning(as.top_ent+as.cur_row);
  5832.                 break;
  5833.         }
  5834.  
  5835.         if(!is_addr(as.top_ent+as.cur_row)){
  5836.                 q_status_message(SM_ORDER, 0, 4, "Nothing to forward");
  5837.                 break;
  5838.         }
  5839.  
  5840.         dl = dlist(as.top_ent+as.cur_row);
  5841.         if(dl->type != ListHead && dl->type != Simple){
  5842.                 q_status_message(SM_ORDER, 0, 4,
  5843.             "Can only forward whole entries");
  5844.                 break;
  5845.         }
  5846.  
  5847.         ab_forward(ps, as.top_ent+as.cur_row);
  5848.             ps->mangled_footer = 1;
  5849.             break;
  5850.  
  5851.  
  5852.           case KEY_RESIZE:
  5853.           case ctrl('L'):
  5854.         mark_status_dirty();
  5855.         mark_titlebar_dirty();
  5856.         mark_keymenu_dirty();
  5857.         ClearBody();
  5858.             ps->mangled_screen = 1;
  5859.         if(c == KEY_RESIZE)
  5860.           ab_resize();
  5861.  
  5862.         break;
  5863.  
  5864.  
  5865.       /* backwards compatibility message */
  5866.       case 'z':
  5867.         if(!are_selecting)
  5868.           q_status_message(SM_ORDER | SM_DING, 0, 2,
  5869.          "Command \"Z\" not defined.  Use \"View/Edit\" to add to a list");
  5870.         else
  5871.           goto bleep;
  5872.  
  5873.         break;
  5874.  
  5875.       default:
  5876.           bleep:
  5877.         bogus_command(orig_c, F_ON(F_USE_FK,ps_global) ? "F1" : "?");
  5878.         break;
  5879.     }
  5880.     }
  5881.     
  5882.     return NULL;
  5883. }
  5884.  
  5885.  
  5886. /*
  5887.  * Post a readonly addrbook warning.
  5888.  *
  5889.  * Args: bell -- Ring the bell
  5890.  *       name -- Include this addrbook name in warning.
  5891.  */
  5892. void
  5893. readonly_warning(bell, name)
  5894.     int        bell;
  5895.     char      *name;
  5896. {
  5897.     q_status_message2(SM_ORDER | (bell ? SM_DING : 0), 0, 4,
  5898.               "AddressBook%s%s is Read Only",
  5899.               name ? " " : "",
  5900.               name ? name : "");
  5901. }
  5902.  
  5903.  
  5904. /*
  5905.  * Post an empty addrbook warning.
  5906.  *
  5907.  * Args: cur_line     -- The current line position (in global display list)
  5908.  *             of cursor
  5909.  */
  5910. void
  5911. empty_warning(cur_line)
  5912.     long cur_line;
  5913. {
  5914.     register AddrScrn_Disp *dl;
  5915.  
  5916.     dl = dlist(cur_line);
  5917.     q_status_message1(SM_ORDER, 0, 4, "%s is Empty", dl->type == Empty
  5918.                         ? "Address Book"
  5919.                         : "Distribution List");
  5920. }
  5921.  
  5922.  
  5923. /*
  5924.  * Tell user to click on this to expand.
  5925.  *
  5926.  * Args: cur_line     -- The current line position (in global display list)
  5927.  *             of cursor
  5928.  */
  5929. void
  5930. clickable_warning(cur_line)
  5931.     long cur_line;
  5932. {
  5933.     register AddrScrn_Disp *dl;
  5934.  
  5935.     dl = dlist(cur_line);
  5936.     q_status_message1(SM_ORDER, 0, 4, "%s not expanded, use \"S\" to expand",
  5937.     dl->type == ClickHere ? "Address Book"
  5938.                   : "Distribution List");
  5939. }
  5940.  
  5941.  
  5942. /*
  5943.  * Post a cancellation warning.
  5944.  *
  5945.  * Args: bell -- Ring the bell
  5946.  *       what -- Text to display
  5947.  */
  5948. void
  5949. cancel_warning(bell, what)
  5950.     int   bell;
  5951.     char *what;
  5952. {
  5953.     q_status_message1(SM_INFO | (bell ? SM_DING : 0), 0, 2,
  5954.               "Address book %s cancelled", what);
  5955. }
  5956.  
  5957.  
  5958. /*
  5959.  * Post a no tabs warning.
  5960.  */
  5961. void
  5962. no_tabs_warning()
  5963. {
  5964.     q_status_message(SM_ORDER, 0, 4, "Tabs not allowed in address book");
  5965. }
  5966.  
  5967.  
  5968. /*
  5969.  * 1522 encode the personal name portion of addr and return an allocated
  5970.  * copy of the resulting address string.
  5971.  */
  5972. char *
  5973. encode_fullname_of_addrstring(addr, charset)
  5974.     char *addr;
  5975.     char *charset;
  5976. {
  5977.     char    *pers_encoded,
  5978.             *tmp_a_string,
  5979.             *ret = NULL;
  5980.     ADDRESS *adr;
  5981.     static char *fakedomain = "@";
  5982.  
  5983.     tmp_a_string = cpystr(addr ? addr : "");
  5984.     adr = NULL;
  5985.     rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
  5986.     fs_give((void **)&tmp_a_string);
  5987.  
  5988.     if(!adr)
  5989.       return(cpystr(""));
  5990.  
  5991.     if(adr->personal && adr->personal[0]){
  5992.     pers_encoded = cpystr(rfc1522_encode(tmp_20k_buf,
  5993.                 (unsigned char *)adr->personal,
  5994.                 charset));
  5995.     fs_give((void **)&adr->personal);
  5996.     adr->personal = pers_encoded;
  5997.     }
  5998.  
  5999.     ret = (char *)fs_get((size_t)est_size(adr));
  6000.     ret[0] = '\0';
  6001.     rfc822_write_address(ret, adr);
  6002.     mail_free_address(&adr);
  6003.     return(ret);
  6004. }
  6005.  
  6006.  
  6007. /*
  6008.  * 1522 decode the personal name portion of addr and return an allocated
  6009.  * copy of the resulting address string.
  6010.  */
  6011. char *
  6012. decode_fullname_of_addrstring(addr, verbose)
  6013.     char *addr;
  6014.     int   verbose;
  6015. {
  6016.     char    *pers_decoded,
  6017.             *tmp_a_string,
  6018.             *ret = NULL,
  6019.         *dummy = NULL;
  6020.     ADDRESS *adr;
  6021.     static char *fakedomain = "@";
  6022.  
  6023.     tmp_a_string = cpystr(addr ? addr : "");
  6024.     adr = NULL;
  6025.     rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
  6026.     fs_give((void **)&tmp_a_string);
  6027.  
  6028.     if(!adr)
  6029.       return(cpystr(""));
  6030.  
  6031.     if(adr->personal && adr->personal[0]){
  6032.     pers_decoded
  6033.         = cpystr((char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  6034.                         adr->personal,
  6035.                         verbose ? NULL : &dummy));
  6036.     fs_give((void **)&adr->personal);
  6037.     adr->personal = pers_decoded;
  6038.     if(dummy)
  6039.       fs_give((void **)&dummy);
  6040.     }
  6041.  
  6042.     ret = (char *)fs_get((size_t)est_size(adr));
  6043.     ret[0] = '\0';
  6044.     rfc822_write_address(ret, adr);
  6045.     mail_free_address(&adr);
  6046.     return(ret);
  6047. }
  6048.  
  6049.  
  6050. /*
  6051.  * Give expanded view of this address entry.
  6052.  * Call scrolltool to do the work.
  6053.  *
  6054.  * Args: headents -- The headerentry array from pico.
  6055.  */
  6056. void
  6057. expand_addrs_for_pico(headents)
  6058.     struct headerentry *headents;
  6059. {
  6060.     BuildTo      bldto;
  6061.     STORE_S     *store;
  6062.     char        *error = NULL, *addr = NULL;
  6063.     char         spaces[40], buf[MAX_ADDRESS+1];
  6064.     SAVE_STATE_S state;
  6065.     ADDRESS     *adrlist = NULL, *a;
  6066.     int          j, address_index;
  6067.     void       (*redraw)() = ps_global->redrawer;
  6068.  
  6069.     dprint(2, (debugfile, "- expand_addrs_for_pico -\n"));
  6070.  
  6071.     ps_global->redrawer = NULL;
  6072.     fix_windsize(ps_global);
  6073.  
  6074.     save_state(&state);
  6075.  
  6076.     for(j=0; headents[j].name != NULL; j++)
  6077.       if(strncmp(headents[j].name, "Address", 7) == 0)
  6078.     break;
  6079.  
  6080.     address_index = j;
  6081.     if(headents[address_index].name == NULL)
  6082.       panic("programmer botch in expand_addrs_for_pico");
  6083.  
  6084.     bldto.type    = Str;
  6085.     bldto.arg.str = *headents[address_index].realaddr;
  6086.     our_build_address(bldto, &addr, &error, NULL, 0, 0);
  6087.     if(error){
  6088.     q_status_message1(SM_ORDER, 3, 4, "%s", error);
  6089.     fs_give((void **)&error);
  6090.     }
  6091.     
  6092.     if(addr)
  6093.       rfc822_parse_adrlist(&adrlist, addr, ps_global->maildomain);
  6094.     
  6095.     if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
  6096.     q_status_message(SM_ORDER | SM_DING, 3, 3, "Error allocating space.");
  6097.     restore_state(&state);
  6098.     return;
  6099.     }
  6100.  
  6101.     for(j = 0; j < address_index; j++){
  6102.     so_puts(store, headents[j].prompt);
  6103.     so_puts(store, *headents[j].realaddr);
  6104.     so_puts(store, "\n");
  6105.     }
  6106.  
  6107.     so_puts(store, headents[address_index].prompt);
  6108.  
  6109.     a = adrlist;
  6110.     if(!a)
  6111.       so_puts(store, "<none>");
  6112.     else{
  6113.     so_puts(store, (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  6114.                             addr_string(a, buf), NULL));
  6115.     a = a->next;
  6116.     }
  6117.  
  6118.     if(a)
  6119.       sprintf(spaces, ",\n%*s", headents[address_index].prlen, " ");
  6120.  
  6121.     for(; a; a = a->next){
  6122.     so_puts(store, spaces);
  6123.     so_puts(store, (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  6124.                             addr_string(a, buf), NULL));
  6125.     }
  6126.  
  6127.     so_puts(store, "\n");
  6128.     scrolltool(so_text(store), "ADDRESS BOOK (Rich View)", ViewAbookText,
  6129.            CharStar, NULL);
  6130.     so_give(&store);
  6131.  
  6132.     restore_state(&state);
  6133.  
  6134.     if(addr)
  6135.       fs_give((void **)&addr);
  6136.     
  6137.     if(adrlist)
  6138.       mail_free_address(&adrlist);
  6139.  
  6140.     ps_global->redrawer = redraw;
  6141. }
  6142.  
  6143.  
  6144. static long        msgno_for_pico_callback;
  6145. static BODY       *body_for_pico_callback = NULL;
  6146. static ENVELOPE   *env_for_pico_callback = NULL;
  6147.  
  6148. /*
  6149.  * Callback from TakeAddr editing screen to see message that was being
  6150.  * viewed.  Call scrolltool to do the work.
  6151.  */
  6152. char *
  6153. view_message_for_pico()
  6154. {
  6155.     STORE_S     *store;
  6156.     gf_io_t      pc;
  6157.     void       (*redraw)() = ps_global->redrawer;
  6158.     SourceType   src = CharStar;
  6159.  
  6160.     dprint(2, (debugfile, "- view_message_for_pico -\n"));
  6161.  
  6162.     ps_global->redrawer = NULL;
  6163.     fix_windsize(ps_global);
  6164.  
  6165. #ifdef DOS
  6166.     src = FileStar;
  6167. #endif
  6168.  
  6169.     if(!(store = so_get(src, NULL, EDIT_ACCESS))){
  6170.     q_status_message(SM_ORDER | SM_DING, 3, 3, "Error allocating space.");
  6171.     return(NULL);
  6172.     }
  6173.  
  6174.     gf_set_so_writec(&pc, store);
  6175.  
  6176.     format_message(msgno_for_pico_callback, env_for_pico_callback,
  6177.            body_for_pico_callback, FM_NEW_MESS, pc);
  6178.  
  6179.     scrolltool(so_text(store), "MESSAGE TEXT", ViewAbookText,
  6180.            src, NULL);
  6181.     so_give(&store);
  6182.  
  6183.     ps_global->redrawer = redraw;
  6184.  
  6185.     return(NULL);
  6186. }
  6187.  
  6188.  
  6189. /*
  6190. prompt::name::help::prlen::maxlen::realaddr::
  6191. builder::affected_entry::next_affected::selector::key_label::
  6192. display_it::break_on_comma::is_attach::rich_header::only_file_chars::
  6193. single_space::sticky::dirty::start_here::KS_ODATAVAR
  6194. */
  6195. static struct headerentry headents_templ[]={
  6196.   {"Nickname  : ",  "Nickname",  h_composer_abook_nick, 12, 0, NULL,
  6197.    verify_nick,   NULL, NULL, addr_book_nick_for_edit, "To AddrBk",
  6198.    1, 0, 0, 0, 0, 1, 0, 0, 0, KS_NONE},
  6199.   {"Fullname  : ",  "Fullname",  h_composer_abook_full, 12, 0, NULL,
  6200.    NULL,          NULL, NULL, view_message_for_pico,   "To Message",
  6201.    1, 0, 0, 0, 0, 1, 0, 0, 0, KS_NONE},
  6202.   {"Fcc       : ",  "FileCopy",  h_composer_abook_fcc, 12, 0, NULL,
  6203.    NULL,          NULL, NULL, folders_for_fcc,         "To Fldrs",
  6204.    1, 0, 0, 0, 0, 1, 0, 0, 0, KS_NONE},
  6205.   {"Comment   : ",  "Comment",   h_composer_abook_comment, 12, 0, NULL,
  6206.    NULL,          NULL, NULL, view_message_for_pico,   "To Message",
  6207.    1, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  6208.   {"Addresses : ",  "Addresses", h_composer_abook_addrs, 12, 0, NULL,
  6209.    verify_addr,   NULL, NULL, addr_book_change_list,   "To AddrBk",
  6210.    1, 1, 0, 0, 0, 1, 0, 0, 0, KS_NONE},
  6211.   {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL,
  6212.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
  6213. };
  6214. #define N_NICK    0
  6215. #define N_FULL    1
  6216. #define N_FCC     2
  6217. #define N_COMMENT 3
  6218. #define N_ADDR    4
  6219. #define N_END     5
  6220.  
  6221. static char        *nick_saved_for_pico_check;
  6222. static AdrBk       *abook_saved_for_pico_check;
  6223. static SAVE_STATE_S state_saved_for_pico_check;
  6224.  
  6225. /*
  6226.  * Args: abook  -- Address book handle
  6227.  *       abe    -- AdrBk_Entry of old entry to work on.  If NULL, this will
  6228.  *                 be a new entry.  This has to be a pointer to a copy of
  6229.  *                 an abe that won't go away until we finish this function.
  6230.  *                 In other words, don't pass in a pointer to an abe in
  6231.  *                 the cache, copy it first.  The tag on this abe is only
  6232.  *                 used to decide whether to read abe->addr.list or
  6233.  *                 abe->addr.addr, not to determine what the final result
  6234.  *                 will be.  That's determined solely by how many addresses
  6235.  *                 there are after the user edits.
  6236.  *       entry  -- The entry number of the old entry that we will be changing.
  6237.  *      old_tag -- If we're changing an old entry, then this is the tag of
  6238.  *                 that old entry.
  6239.  *     readonly -- Call pico with readonly flag
  6240.  *       warped -- We warped to a new part of the addrbook
  6241.  *                 (We also overload warped in a couple places and use it's
  6242.  *                  being set as an indicator of whether we are Taking or
  6243.  *                  not.  It will be NULL if we are Taking.)
  6244.  */
  6245. void
  6246. edit_entry(abook, abe, entry, old_tag, readonly, warped)
  6247.     AdrBk       *abook;
  6248.     AdrBk_Entry *abe;
  6249.     a_c_arg_t    entry;
  6250.     Tag          old_tag;
  6251.     int          readonly;
  6252.     int         *warped;
  6253. {
  6254.     AdrBk_Entry local_abe;
  6255.     struct headerentry *he;
  6256.     PICO pbuf;
  6257.     STORE_S *msgso;
  6258.     adrbk_cntr_t old_entry_num, new_entry_num = NO_NEXT;
  6259.     int rc = 0, resort_happened = 0, list_changed = 0, which_addrbook;
  6260.     int editor_result, i = 0, add;
  6261.     char *nick, *full, *fcc, *comment, *dcomment, *fname, *dummy, *pp;
  6262.     char **orig_encoded = NULL, **orig_decoded = NULL;
  6263.     char **new_encoded = NULL, **new_decoded = NULL;
  6264.     char *addr_encoded  = NULL, *addr_decoded = NULL;
  6265.     char **p, **q;
  6266.     Tag new_tag;
  6267.     char titlebar[30];
  6268.     long length;
  6269.  
  6270.     dprint(2, (debugfile, "- edit_entry -\n"));
  6271.  
  6272.     old_entry_num = (adrbk_cntr_t)entry;
  6273.     save_state(&state_saved_for_pico_check);
  6274.     abook_saved_for_pico_check = abook;
  6275.  
  6276.     add = (abe == NULL);  /* doing add or change? */
  6277.     if(add){
  6278.     local_abe.nickname  = "";
  6279.     local_abe.fullname  = "";
  6280.     local_abe.fcc       = "";
  6281.     local_abe.extra     = "";
  6282.     local_abe.addr.addr = "";
  6283.     local_abe.tag       = NotSet;
  6284.     abe = &local_abe;
  6285.     old_entry_num = NO_NEXT;
  6286.     }
  6287.  
  6288.     new_tag = abe->tag;
  6289.  
  6290.     memset((void *)&pbuf, 0, sizeof(pbuf));
  6291.     pbuf.raw_io        = Raw;
  6292.     pbuf.showmsg       = display_message_for_pico;
  6293.     pbuf.newmail       = new_mail_for_pico;
  6294.     pbuf.msgntext      = NULL;
  6295.     pbuf.upload           = NULL;
  6296.     pbuf.ckptdir       = checkpoint_dir_for_pico;
  6297.     pbuf.mimetype      = NULL;
  6298.     pbuf.exittest      = pico_sendexit_for_adrbk;
  6299.     pbuf.canceltest    = warped ? pico_cancel_for_adrbk_edit
  6300.                 : pico_cancel_for_adrbk_take;
  6301.     pbuf.expander      = expand_addrs_for_pico;
  6302.     pbuf.resize           = resize_for_pico;
  6303.     pbuf.keybinit      = init_keyboard;
  6304.     pbuf.helper        = helper;
  6305.     pbuf.alt_ed        = NULL;
  6306.     pbuf.alt_spell     = NULL;
  6307.     pbuf.quote_str     = "";
  6308.     pbuf.fillcolumn    = ps_global->composer_fillcol;
  6309.     pbuf.menu_rows     = FOOTER_ROWS(ps_global) - 1;
  6310.     pbuf.ins_help      = h_composer_ins;
  6311.     pbuf.search_help   = h_composer_search;
  6312.     pbuf.browse_help   = h_composer_browse;
  6313.     pbuf.composer_help = h_composer;
  6314.     sprintf(titlebar, "ADDRESS BOOK (%s)", readonly ? "View" : "Edit");
  6315.     pbuf.pine_anchor   = set_titlebar(titlebar,
  6316.                       ps_global->mail_stream,
  6317.                       ps_global->context_current,
  6318.                       ps_global->cur_folder,ps_global->msgmap, 
  6319.                       0, FolderName, 0, 0);
  6320.     pbuf.pine_version  = pine_version;
  6321.     pbuf.pine_flags    = flags_for_pico(ps_global);
  6322.     pbuf.pine_flags   |= P_ABOOK;
  6323.     if(readonly)
  6324.       pbuf.pine_flags |= P_VIEW;
  6325.  
  6326.     /* An informational message */
  6327.     if(msgso = so_get(PicoText, NULL, EDIT_ACCESS)){
  6328.     pbuf.msgtext = (void *)so_text(msgso);
  6329.     /*
  6330.      * It's nice if we can make it so these lines make sense even if
  6331.      * they don't all make it on the screen, because the user can't
  6332.      * scroll down to see them.  So just make each line a whole sentence
  6333.      * that doesn't need the others below it to make sense.
  6334.      */
  6335.     if(add){
  6336.         so_puts(msgso,
  6337. "\n Fill in the fields just like you would in the composer.");
  6338.         so_puts(msgso,
  6339. "\n To form a list, just enter multiple comma-separated addresses.");
  6340.         so_puts(msgso,
  6341. "\n To add to a list, use the View/Edit cmd instead of the AddNew cmd.");
  6342.     }
  6343.     else{
  6344.         so_puts(msgso,
  6345. "\n Edit any of the fields, just like you would do in the composer.");
  6346.         so_puts(msgso,
  6347. "\n Additional comma-separated addresses may be entered in the address field.");
  6348.     }
  6349.  
  6350.     so_puts(msgso,
  6351. "\n It is ok to leave fields blank.  Press ^X to save the new entry.");
  6352.     }
  6353.  
  6354.     he = (struct headerentry *)fs_get((N_END+1) * sizeof(struct headerentry));
  6355.     memset((void *)he, 0, (N_END+1) * sizeof(struct headerentry));
  6356.     pbuf.headents      = he;
  6357.  
  6358.     /* make a copy of each field */
  6359.     nick = cpystr(abe->nickname);
  6360.     nick_saved_for_pico_check = cpystr(abe->nickname);
  6361.     he[N_NICK]          = headents_templ[N_NICK];
  6362.     he[N_NICK].realaddr = &nick;
  6363.  
  6364.     dummy = NULL;
  6365.     full = cpystr((char *)rfc1522_decode((unsigned char *)tmp_20k_buf+10000,
  6366.                    abe->fullname, &dummy));
  6367.     if(dummy)
  6368.       fs_give((void **)&dummy);
  6369.  
  6370.     he[N_FULL]          = headents_templ[N_FULL];
  6371.     he[N_FULL].realaddr = &full;
  6372.  
  6373.     fcc = cpystr(abe->fcc);
  6374.     he[N_FCC]          = headents_templ[N_FCC];
  6375.     he[N_FCC].realaddr = &fcc;
  6376.  
  6377.     dummy = NULL;
  6378.     dcomment = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf, abe->extra,
  6379.                                     &dummy);
  6380.     if(dummy)
  6381.       fs_give((void **)&dummy);
  6382.  
  6383.     comment = cpystr(dcomment);
  6384.     he[N_COMMENT]          = headents_templ[N_COMMENT];
  6385.     he[N_COMMENT].realaddr = &comment;
  6386.  
  6387.     he[N_ADDR]          = headents_templ[N_ADDR];
  6388.     he[N_ADDR].realaddr = &addr_decoded;
  6389.     if(abe->tag == Single){
  6390.     if(abe->addr.addr){
  6391.         orig_encoded = (char **)fs_get(2 * sizeof(char *));
  6392.         orig_decoded = (char **)fs_get(2 * sizeof(char *));
  6393.         orig_encoded[0] = cpystr(abe->addr.addr);
  6394.         orig_encoded[1] = NULL;
  6395.     }
  6396.     }
  6397.     else if(abe->tag == List){
  6398.     if(listmem_count_from_abe(abe) > 0){
  6399.         orig_encoded = (char **)fs_get(
  6400.                       (size_t)(listmem_count_from_abe(abe) + 1)
  6401.                     * sizeof(char *));
  6402.         orig_decoded = (char **)fs_get(
  6403.                       (size_t)(listmem_count_from_abe(abe) + 1)
  6404.                     * sizeof(char *));
  6405.         for(q = orig_encoded, p = abe->addr.list; p && *p; p++, q++)
  6406.           *q = cpystr(*p);
  6407.         
  6408.         *q = NULL;
  6409.     }
  6410.     }
  6411.  
  6412.     /*
  6413.      * Orig_encoded is the original list saved in encoded form.
  6414.      * Now save the original list but in decoded form.
  6415.      */
  6416.     for(q = orig_decoded, p = orig_encoded; p && *p; p++, q++){
  6417.     /*
  6418.      * Here we have an address string, which we need to parse, then
  6419.      * decode the fullname, possibly quote it, then turn it back into
  6420.      * a string.
  6421.      */
  6422.     *q = decode_fullname_of_addrstring(*p, 0);
  6423.     }
  6424.  
  6425.     if(q)
  6426.       *q = NULL;
  6427.  
  6428.     /* figure out how large a string we need to allocate */
  6429.     length = 0L;
  6430.     for(p = orig_decoded; p && *p; p++)
  6431.       length += (strlen(*p) + 2);
  6432.     
  6433.     if(length)
  6434.       length -= 2L;
  6435.  
  6436.     pp = addr_decoded = (char *)fs_get((size_t)(length+1L) * sizeof(char));
  6437.     *pp = '\0';
  6438.     for(p = orig_decoded; p && *p; p++){
  6439.     sstrcpy(&pp, *p);
  6440.     if(*(p+1))
  6441.       sstrcpy(&pp, ", ");
  6442.     }
  6443.  
  6444.     if(verify_addr(addr_decoded, NULL, NULL, NULL) < 0)
  6445.       he[N_ADDR].start_here = 1;
  6446.  
  6447.     /*
  6448.      * Now we have orig_encoded -- a list of encoded addresses
  6449.      *             orig_decoded -- a list of decoded addresses
  6450.      *             addr_decoded -- orig_decoded put together into a decoded,
  6451.      *                    comma-separated string
  6452.      *             new_decoded will be the edited, decoded list
  6453.      *             new_encoded will be the encoded version of that
  6454.      */
  6455.  
  6456.     he[N_END] = headents_templ[N_END];
  6457.     for(i = 0; i < N_END; i++){
  6458.     /* no callbacks in some cases */
  6459.     if(readonly || ((i == N_FULL || i == N_COMMENT)
  6460.             && !env_for_pico_callback)){
  6461.         he[i].selector  = NULL;
  6462.         he[i].key_label = NULL;
  6463.     }
  6464.  
  6465.     /* no builders for readonly */
  6466.     if(readonly)
  6467.       he[i].builder = NULL;
  6468.     }
  6469.  
  6470.     /* pass to pico and let user change them */
  6471.     editor_result = pico(&pbuf);
  6472.  
  6473.     if(editor_result & COMP_GOTHUP)
  6474.       hup_signal();
  6475.     else{
  6476.     fix_windsize(ps_global);
  6477.     init_signals();
  6478.     }
  6479.  
  6480.     if(editor_result & COMP_CANCEL){
  6481.     if(!readonly)
  6482.       q_status_message1(SM_ORDER, 0, 3, "%s is cancelled",
  6483.                 warped ? "Edit" : "Take");
  6484.     }
  6485.     else if(editor_result & COMP_EXIT){
  6486.     /* don't allow adding null entry */
  6487.     if(add && !*nick && !*full && !*fcc && !*comment && !*addr_decoded)
  6488.       goto outtahere;
  6489.  
  6490.     /*
  6491.      * addr_decoded is now the decoded string which has been edited
  6492.      */
  6493.     if(addr_decoded)
  6494.       new_decoded = parse_addrlist(addr_decoded);
  6495.  
  6496.     if(!new_decoded || !new_decoded[0])
  6497.       q_status_message(SM_ORDER, 3, 5, "Warning: entry has no addresses");
  6498.  
  6499.     if(!new_decoded || !new_decoded[0] || !new_decoded[1])
  6500.       new_tag = Single;  /* one or zero addresses means its a Single */
  6501.     else
  6502.       new_tag = List;    /* more than one addresses means its a List */
  6503.  
  6504.     if(new_tag == List && old_tag == List){
  6505.         /*
  6506.          * If Taking, make sure we write it even if user didn't edit
  6507.          * it any further.
  6508.          */
  6509.         if(!warped)
  6510.           list_changed++;
  6511.         else if(he[N_ADDR].dirty)
  6512.           for(q = orig_decoded, p = new_decoded; p && *p && q && *q; p++, q++)
  6513.             if(strcmp(*p, *q) != 0){
  6514.             list_changed++;
  6515.             break;
  6516.             }
  6517.         
  6518.         if(!list_changed && he[N_ADDR].dirty
  6519.           && ((!(p && *p) && (q && *q)) || ((p && *p) && !(q && *q))))
  6520.           list_changed++;
  6521.  
  6522.         if(list_changed){
  6523.         /*
  6524.          * need to delete old list members and add new members below
  6525.          */
  6526.         rc = adrbk_listdel_all(abook, (a_c_arg_t)old_entry_num);
  6527.         }
  6528.         else{
  6529.         /* don't need new_decoded */
  6530.         if(new_decoded)
  6531.           free_list(&new_decoded);
  6532.         }
  6533.  
  6534.         if(addr_decoded)
  6535.           fs_give((void **)&addr_decoded);
  6536.     }
  6537.     else if(new_tag == List && old_tag == Single
  6538.            || new_tag == Single && old_tag == List){
  6539.         /* delete old entry */
  6540.         rc = adrbk_delete(abook, (a_c_arg_t)old_entry_num, 0);
  6541.         old_entry_num = NO_NEXT;
  6542.         if(addr_decoded && new_tag == List)
  6543.           fs_give((void **)&addr_decoded);
  6544.     }
  6545.  
  6546.     if(new_tag == Single && addr_decoded){
  6547.         /*
  6548.          * Compare addr_decoded to each of orig_decoded.
  6549.          * If it matches one, make addr_encoded equal to orig_encoded
  6550.          * for that one, else encode it in our charset.
  6551.          */
  6552.         for(q = orig_decoded, p = orig_encoded; q && *q; q++, p++){
  6553.         if(!he[N_ADDR].dirty || strcmp(*q, addr_decoded) == 0)
  6554.           break;
  6555.         }
  6556.  
  6557.         if(q && *q && p && *p)  /* got a match, use what we already had */
  6558.           addr_encoded = cpystr(*p);
  6559.         else  /* encode in our charset */
  6560.           addr_encoded = encode_fullname_of_addrstring(addr_decoded,
  6561.                               ps_global->VAR_CHAR_SET);
  6562.     }
  6563.  
  6564.     /*
  6565.      * This will be an edit in the cases where the tag didn't change
  6566.      * and an add in the cases where it did.
  6567.      */
  6568.     if(rc == 0)
  6569.       rc = adrbk_add(abook,
  6570.                (a_c_arg_t)old_entry_num,
  6571.                nick,
  6572.                he[N_FULL].dirty ? rfc1522_encode(tmp_20k_buf,
  6573.                                (unsigned char *)full,
  6574.                                ps_global->VAR_CHAR_SET)
  6575.                     : abe->fullname,
  6576.                new_tag == Single ? addr_encoded : NULL,
  6577.                fcc,
  6578.                he[N_COMMENT].dirty ? rfc1522_encode(tmp_20k_buf +
  6579.                                    2*MAX_FULLNAME,
  6580.                                (unsigned char *)comment,
  6581.                                ps_global->VAR_CHAR_SET)
  6582.                        : abe->extra,
  6583.                new_tag,
  6584.                &new_entry_num,
  6585.                &resort_happened);
  6586.     }
  6587.     
  6588.     if(rc == 0 && new_tag == List && new_decoded){
  6589.     char **t;
  6590.  
  6591.     /*
  6592.      * Build a new list.
  6593.      * For each entry in new_decoded, look through orig_decoded.  If
  6594.      * matched, go with that orig_encoded, else encode that entry.
  6595.      */
  6596.     for(p = new_decoded; *p; p++)
  6597.       ;/* just counting for alloc below */
  6598.     
  6599.     t = new_encoded = (char **)fs_get((size_t)((p - new_decoded) + 1)
  6600.                                 * sizeof(char *));
  6601.     memset((void *)new_encoded, 0, ((p-new_decoded)+1) * sizeof(char *));
  6602.     for(p = new_decoded; p && *p; p++){
  6603.         for(q = orig_decoded; q && *q; q++)
  6604.           if(strcmp(*q, *p) == 0)
  6605.         break;
  6606.  
  6607.         if(q && *q)  /* got a match, use what we already have */
  6608.           *t++ = cpystr(orig_encoded[q - orig_decoded]);
  6609.         else  /* encode in our charset */
  6610.           *t++ = encode_fullname_of_addrstring(*p, ps_global->VAR_CHAR_SET);
  6611.     }
  6612.  
  6613.     rc = adrbk_nlistadd(abook, (a_c_arg_t)new_entry_num, new_encoded);
  6614.     }
  6615.  
  6616.     restore_state(&state_saved_for_pico_check);
  6617.  
  6618.     if(rc == -2 || rc == -3){
  6619.     q_status_message1(SM_ORDER | SM_DING, 3, 4,
  6620.             "Error updating address book: %s",
  6621.             rc == -2 ? error_description(errno) : "Pine bug");
  6622.     }
  6623.     else if(rc == 0
  6624.        && strucmp(nick, nick_saved_for_pico_check) != 0
  6625.        && (editor_result & COMP_EXIT)
  6626.        && (fname = addr_lookup(nick, &which_addrbook, as.cur))){
  6627.     q_status_message4(SM_ORDER, 5, 9,
  6628.         "Warning! nickname %s also exists in \"%s\"%s%s",
  6629.         nick, as.adrbks[which_addrbook].nickname,
  6630.         (fname && *fname) ? " as " : "",
  6631.         (fname && *fname) ? fname : "");
  6632.     if(fname)
  6633.       fs_give((void **)&fname);
  6634.     }
  6635.  
  6636.     if(resort_happened || list_changed){
  6637.     DL_CACHE_S dlc_restart;
  6638.  
  6639.     dlc_restart.adrbk_num = as.cur;
  6640.     dlc_restart.dlcelnum  = new_entry_num;
  6641.     switch(new_tag){
  6642.       case Single:
  6643.         dlc_restart.type = DlcSimple;
  6644.         break;
  6645.       
  6646.       case List:
  6647.         dlc_restart.type = DlcListHead;
  6648.         break;
  6649.     }
  6650.  
  6651.     warp_to_dlc(&dlc_restart, 0L);
  6652.     if(warped)
  6653.       *warped = 1;
  6654.     }
  6655.  
  6656. outtahere:
  6657.     if(he){
  6658.     for(i = 0; i < N_END; i++)
  6659.       if(he[i].bldr_private)
  6660.       fs_give((void **)&(he[i].bldr_private));
  6661.  
  6662.     fs_give((void **)&he);
  6663.     }
  6664.  
  6665.     if(msgso)
  6666.       so_give(&msgso);
  6667.  
  6668.     if(nick)
  6669.       fs_give((void **)&nick);
  6670.     if(full)
  6671.       fs_give((void **)&full);
  6672.     if(fcc)
  6673.       fs_give((void **)&fcc);
  6674.     if(comment)
  6675.       fs_give((void **)&comment);
  6676.  
  6677.     if(addr_decoded)
  6678.       fs_give((void **)&addr_decoded);
  6679.     if(addr_encoded)
  6680.       fs_give((void **)&addr_encoded);
  6681.     if(nick_saved_for_pico_check)
  6682.       fs_give((void **)&nick_saved_for_pico_check);
  6683.  
  6684.     if(orig_decoded)
  6685.       free_list(&orig_decoded);
  6686.     if(orig_encoded)
  6687.       free_list(&orig_encoded);
  6688.  
  6689.     if(new_decoded)
  6690.       free_list(&new_decoded);
  6691.     if(new_encoded)
  6692.       free_list(&new_encoded);
  6693. }
  6694.  
  6695.  
  6696. /*ARGSUSED*/
  6697. int
  6698. verify_nick(given, expanded, error, fcc)
  6699.     char     *given,
  6700.         **expanded,
  6701.         **error;
  6702.     BUILDER_ARG  *fcc;
  6703. {
  6704.     char *tmp;
  6705.  
  6706.     tmp = cpystr(given);
  6707.     removing_leading_white_space(tmp);
  6708.     removing_trailing_white_space(tmp);
  6709.  
  6710.     if(nickname_check(tmp, error)){
  6711.     fs_give((void **)&tmp);
  6712.     return -2;
  6713.     }
  6714.  
  6715.     if(strucmp(tmp, nick_saved_for_pico_check) != 0){
  6716.     restore_state(&state_saved_for_pico_check);
  6717.     if(adrbk_lookup_by_nick(abook_saved_for_pico_check,
  6718.                    tmp, (adrbk_cntr_t *)NULL)){
  6719.         if(error){
  6720.         char buf[MAX_NICKNAME + 80];
  6721.  
  6722.         sprintf(buf, "\"%s\" already in address book.", tmp);
  6723.         *error = cpystr(buf);
  6724.         }
  6725.         
  6726.         fs_give((void **)&tmp);
  6727.         save_state(&state_saved_for_pico_check);
  6728.         return -2;
  6729.     }
  6730.  
  6731.     save_state(&state_saved_for_pico_check);
  6732.     }
  6733.  
  6734.     if(expanded)
  6735.       *expanded = tmp;
  6736.  
  6737.     /* This is so pico will erase any old message */
  6738.     if(error)
  6739.       *error = cpystr("");
  6740.  
  6741.     return 0;
  6742. }
  6743.  
  6744.  
  6745. /*
  6746.  * Args: to      -- the passed in line to parse
  6747.  *       full_to -- Address of a pointer to return the full address in.
  6748.  *            This will be allocated here and freed by the caller.
  6749.  *                  However, this function is just going to copy "to".
  6750.  *                  We're just looking for the error messages.
  6751.  *       error   -- Address of a pointer to return an error message in.
  6752.  *            This will be allocated here and freed by the caller.
  6753.  *       fcc     -- This should be passed in NULL.
  6754.  *                  This builder doesn't support affected_entry's.
  6755.  *
  6756.  * Result:  0 is returned if address was OK, 
  6757.  *         -2 if address wasn't OK.
  6758.  *
  6759.  * Side effect: Can flush addrbook entry cache entries so they need to be
  6760.  * re-fetched afterwords.
  6761.  */
  6762. int
  6763. verify_addr(to, full_to, error, fcc)
  6764.     char     *to,
  6765.         **full_to,
  6766.         **error;
  6767.     BUILDER_ARG     *fcc;
  6768. {
  6769.     register char *p;
  6770.     int            ret_val;
  6771.     BuildTo        bldto;
  6772.     jmp_buf        save_jmp_buf;
  6773.  
  6774.     dprint(2, (debugfile, "- verify_addr - (%s)\n", to ? to : "nul"));
  6775.  
  6776.     if(fcc)
  6777.       panic("programmer botch in verify_addr");
  6778.  
  6779.     /* check to see if to string is empty to avoid work */
  6780.     for(p = to; p && *p && isspace((unsigned char)(*p)); p++)
  6781.       ;/* do nothing */
  6782.  
  6783.     if(!p || !*p){
  6784.     if(full_to)
  6785.       *full_to = cpystr(to ? to : "");  /* because pico does a strcmp() */
  6786.  
  6787.     return 0;
  6788.     }
  6789.  
  6790.     if(full_to != NULL)
  6791.       *full_to = (char *)NULL;
  6792.  
  6793.     if(error != NULL)
  6794.       *error = (char *)NULL;
  6795.     
  6796.     /*
  6797.      * If we end up jumping back here because somebody else changed one of
  6798.      * our addrbooks out from underneath us, we may well leak some memory.
  6799.      * That's probably ok since this will be very rare.
  6800.      */
  6801.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  6802.     if(setjmp(addrbook_changed_unexpectedly)){
  6803.     if(full_to && *full_to)
  6804.       fs_give((void **)full_to);
  6805.  
  6806.     q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
  6807.     dprint(1, (debugfile,
  6808.         "RESETTING address book... verify_addr(%s)!\n", to));
  6809.     addrbook_reset();
  6810.     }
  6811.  
  6812.     bldto.type    = Str;
  6813.     bldto.arg.str = to;
  6814.  
  6815.     ret_val = build_address_internal(bldto,NULL,error,NULL,NULL,NULL,1,1);
  6816.  
  6817.     if(full_to){
  6818.     *full_to = cpystr(to ? to : "");
  6819.     if(ret_val >= 0){
  6820.         removing_leading_white_space(*full_to);
  6821.         removing_trailing_white_space(*full_to);
  6822.     }
  6823.     }
  6824.  
  6825.     /* This is so pico will erase the old message */
  6826.     if(error != NULL && *error == NULL)
  6827.       *error = cpystr("");
  6828.  
  6829.     if(ret_val < 0)
  6830.       ret_val = -2;  /* cause pico to stay on same header line */
  6831.  
  6832.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  6833.     return(ret_val);
  6834. }
  6835.  
  6836.  
  6837. /*
  6838.  *  Call back for pico to prompt the user for exit confirmation
  6839.  *
  6840.  * Returns: either NULL if the user accepts exit, or string containing
  6841.  *     reason why the user declined.
  6842.  */      
  6843. char *
  6844. pico_sendexit_for_adrbk()
  6845. {
  6846.     char *rstr = NULL;
  6847.     void (*redraw)() = ps_global->redrawer;
  6848.  
  6849.     ps_global->redrawer = NULL;
  6850.     fix_windsize(ps_global);
  6851.     
  6852.     switch(want_to("Exit and save changes ", 'y', 0, NO_HELP, 0, 0)){
  6853.       case 'y':
  6854.     break;
  6855.  
  6856.       case 'n':
  6857.     rstr = "Use ^C to abandon changes you've made";
  6858.     break;
  6859.  
  6860. #ifdef OLDWAY
  6861.       /* ^C */
  6862.       case 'x':
  6863.     rstr = "";
  6864.     break;
  6865. #endif
  6866.     }
  6867.  
  6868.     ps_global->redrawer = redraw;
  6869.     return(rstr);
  6870. }
  6871.  
  6872.  
  6873. /*
  6874.  *  Call back for pico to prompt the user for exit confirmation
  6875.  *
  6876.  * Returns: either NULL if the user accepts exit, or string containing
  6877.  *     reason why the user declined.
  6878.  */      
  6879. char *
  6880. pico_cancelexit_for_adrbk(word)
  6881.     char *word;
  6882. {
  6883.     char prompt[90];
  6884.     char *rstr = NULL;
  6885.     void (*redraw)() = ps_global->redrawer;
  6886.  
  6887.     strcat(strcat(strcpy(prompt, "Cancel "), word),
  6888.        " (answering \"Yes\" will abandon any changes made) ");
  6889.     ps_global->redrawer = NULL;
  6890.     fix_windsize(ps_global);
  6891.     
  6892.     switch(want_to(prompt, 'y', 'x', NO_HELP, 0, 0)){
  6893.       case 'y':
  6894.     rstr = "";
  6895.     break;
  6896.  
  6897.       case 'n':
  6898.       case 'x':
  6899.     break;
  6900.     }
  6901.  
  6902.     ps_global->redrawer = redraw;
  6903.     return(rstr);
  6904. }
  6905.  
  6906.  
  6907. char *
  6908. pico_cancel_for_adrbk_take()
  6909. {
  6910.     return(pico_cancelexit_for_adrbk("take"));
  6911. }
  6912.  
  6913.  
  6914. char *
  6915. pico_cancel_for_adrbk_edit()
  6916. {
  6917.     return(pico_cancelexit_for_adrbk("changes"));
  6918. }
  6919.  
  6920.  
  6921. /*
  6922.  * Validate selected address with build_address, save addrbook state,
  6923.  * call composer, restore addrbook state.
  6924.  *
  6925.  * Args: cur_line     -- The current line position (in global display list)
  6926.  *             of cursor
  6927.  */
  6928. void
  6929. ab_compose_to_addr(cur_line)
  6930.     long cur_line;
  6931. {
  6932.     int           good_addr;
  6933.     char          *addr,
  6934.           *error = NULL,
  6935.           *fcc;
  6936.     AddrScrn_Disp *dl;
  6937.     AdrBk_Entry   *abe;
  6938.     SAVE_STATE_S   state;  /* For saving state of addrbooks temporarily */
  6939.     BuildTo      bldto;
  6940.  
  6941.     dprint(2, (debugfile, "- ab_compose_to_addr -\n"));
  6942.  
  6943.     save_state(&state);
  6944.  
  6945.     fcc  = NULL;
  6946.     addr = NULL;
  6947.  
  6948.     if(is_addr(cur_line)){
  6949.  
  6950.     dl  = dlist(cur_line);
  6951.     abe = ae(cur_line);
  6952.  
  6953.     if(dl->type == ListHead && listmem_count_from_abe(abe) == 0){
  6954.         q_status_message(SM_ORDER, 0, 4, "Warning:  this list is empty!");
  6955.         good_addr = 0;
  6956.     }
  6957.     else if(dl->type == ListEnt){
  6958.         bldto.type    = Str;
  6959.         bldto.arg.str = listmem(cur_line);
  6960.         good_addr = (our_build_address(bldto,&addr,&error,&fcc,0,0) >= 0);
  6961.     }
  6962.     else{
  6963.         bldto.type    = Abe;
  6964.         bldto.arg.abe = abe;
  6965.         good_addr = (our_build_address(bldto,&addr,&error,&fcc,0,0) >= 0);
  6966.     }
  6967.  
  6968.     if(error){
  6969.         q_status_message1(SM_ORDER, 3, 4, "%s", error);
  6970.         fs_give((void **)&error);
  6971.     }
  6972.  
  6973.     if(!good_addr && addr)
  6974.       fs_give((void **)&addr); /* relying on fs_give setting
  6975.                     addr to NULL */
  6976.     }
  6977.  
  6978.     compose_mail(addr, fcc, NULL);
  6979.  
  6980.     restore_state(&state);
  6981.  
  6982.     if(addr)
  6983.       fs_give((void **)&addr);
  6984.  
  6985.     if(fcc)
  6986.       fs_give((void **)&fcc);
  6987. }
  6988.  
  6989.  
  6990. /*
  6991.  * Export the addresses of a list into a file.
  6992.  *
  6993.  * Args: cur_line     -- The current line position (in global display list)
  6994.  *             of cursor
  6995.  *       command_line -- The screen line on which to prompt
  6996.  */
  6997. void
  6998. ab_export(cur_line, command_line)
  6999.     long cur_line;
  7000.     int  command_line;
  7001. {
  7002.     int           good_addr, over = 0;
  7003.     char          *addr,
  7004.           *error = NULL;
  7005.     AddrScrn_Disp *dl;
  7006.     AdrBk_Entry   *abe;
  7007.     BuildTo        bldto;
  7008.     struct variable *vars = ps_global->vars;
  7009.  
  7010.     dprint(2, (debugfile, "- ab_export -\n"));
  7011.  
  7012.     if(ps_global->restricted){
  7013.     q_status_message(SM_ORDER, 0, 3,
  7014.         "Pine demo can't export addresses to files");
  7015.     return;
  7016.     }
  7017.  
  7018.     addr = NULL;
  7019.     dl  = dlist(cur_line);
  7020.     abe = ae(cur_line);
  7021.  
  7022.     if(dl->type == ListHead && listmem_count_from_abe(abe) == 0){
  7023.     error = "List is empty, nothing to export!";
  7024.     good_addr = 0;
  7025.     }
  7026.     else if(dl->type == ListEnt){
  7027.     bldto.type    = Str;
  7028.     bldto.arg.str = listmem(cur_line);
  7029.     good_addr = (our_build_address(bldto,&addr,&error,NULL,0,0) >= 0);
  7030.     }
  7031.     else{
  7032.     bldto.type    = Abe;
  7033.     bldto.arg.abe = abe;
  7034.     good_addr = (our_build_address(bldto,&addr,&error,NULL,0,0) >= 0);
  7035.     }
  7036.  
  7037.     /* Have to rfc1522_decode the addr */
  7038.     if(addr){
  7039.     char *p;
  7040.  
  7041.     p = (char *)fs_get((strlen(addr) + 1) * sizeof(char));
  7042.     if(rfc1522_decode((unsigned char *)p,addr,NULL) == (unsigned char *)p){
  7043.         fs_give((void **)&addr);
  7044.         addr = p;
  7045.     }
  7046.     else
  7047.       fs_give((void **)&p);
  7048.     }
  7049.  
  7050.     if(error){
  7051.     q_status_message1(SM_ORDER, 3, 4, "%s", error);
  7052.     fs_give((void **)&error);
  7053.     }
  7054.  
  7055.     if(good_addr){
  7056.     int   quoted = 0;
  7057.     char *p;
  7058.     char  prompt[100];
  7059.  
  7060.     /*
  7061.      * Change the unquoted commas into newlines.  Not worth it to do
  7062.      * complicated quoting, just consider double quotes.
  7063.      */
  7064.     for(p = addr; *p; p++){
  7065.         if(*p == '"')
  7066.           quoted = !quoted;
  7067.         else if(!quoted && *p == ','){
  7068.         *p++ = '\n';
  7069.         removing_leading_white_space(p);
  7070.         p--;
  7071.         }
  7072.     }
  7073.  
  7074.     if(addr && *addr){
  7075.         static ESCKEY_S export_opts[] = {
  7076.         {ctrl('T'), 10, "^T", "To Files"},
  7077.         {-1, 0, NULL, NULL}};
  7078.         HelpType help;
  7079.         char     filename[MAXPATH+1], full_filename[MAXPATH+1];
  7080.         char    *ill;
  7081.         int      rc, orig_errno, failure = 0;
  7082.         STORE_S *store;
  7083.         gf_io_t  pc;
  7084.         long     start_of_append;
  7085.  
  7086.         help = NO_HELP;
  7087.         filename[0] = '\0';
  7088.         while(1){
  7089. #ifdef    DOS
  7090.         (void)strcpy(prompt, "File to save addresses in: ");
  7091. #else
  7092.         sprintf(prompt,
  7093.             "EXPORT: (copy addresses) to file in %s directory: ",
  7094.              F_ON(F_USE_CURRENT_DIR, ps_global) ? "current"
  7095.              : VAR_OPER_DIR ? VAR_OPER_DIR : "home");
  7096. #endif
  7097.  
  7098.         rc = optionally_enter(filename, command_line, 0, MAXPATH, 1, 0,
  7099.              prompt, export_opts, help, 0);
  7100.  
  7101.         /* file browser */
  7102.         if(rc == 10){
  7103.             if(filename[0])
  7104.               strcpy(full_filename, filename);
  7105.             else if(F_ON(F_USE_CURRENT_DIR, ps_global))
  7106.               (void)getcwd(full_filename, MAXPATH);
  7107.             else if(VAR_OPER_DIR)
  7108.               build_path(full_filename, VAR_OPER_DIR, filename);
  7109.             else
  7110.               build_path(full_filename, ps_global->home_dir, filename);
  7111.  
  7112.             rc = file_lister("EXPORT", full_filename, filename, TRUE,
  7113.                      FB_SAVE);
  7114.  
  7115.             if(rc == 1){
  7116.             strcat(full_filename, "/");
  7117.             strcat(full_filename, filename);
  7118.             break;
  7119.             }
  7120.             else
  7121.               continue;
  7122.         }
  7123.         else if(rc == 3){
  7124.             help = (help == NO_HELP) ? h_oe_export : NO_HELP;
  7125.             continue;
  7126.         }
  7127.  
  7128.         removing_trailing_white_space(filename);
  7129.         removing_leading_white_space(filename);
  7130.         if(rc == 1 || filename[0] == '\0'){
  7131.             q_status_message(SM_ORDER, 0, 2, "Export cancelled");
  7132.             goto fini;
  7133.         }
  7134.  
  7135.         if(rc == 4)
  7136.           continue;
  7137.  
  7138.         /*-- check out and expand file name. give error messages --*/
  7139.         strcpy(full_filename, filename);
  7140.         if((ill = filter_filename(filename)) != NULL){
  7141.             q_status_message1(SM_ORDER | SM_DING, 3, 3, "%s", ill);
  7142.             continue;
  7143.         }
  7144. #if defined(DOS) || defined(OS2)
  7145.         if(is_absolute_path(full_filename)){
  7146.             fixpath(full_filename, MAXPATH);
  7147.         }
  7148. #else
  7149.         if(full_filename[0] == '~'){
  7150.             if(fnexpand(full_filename, sizeof(full_filename)) == NULL){
  7151.             p = strindex(full_filename, '/');
  7152.             if(p != NULL)
  7153.               *p = '\0';
  7154.  
  7155.             q_status_message1(SM_ORDER | SM_DING, 3, 3,
  7156.                   "Error expanding file name: \"%s\" unknown user",
  7157.                       full_filename);
  7158.             continue;
  7159.             }
  7160.         }
  7161. #endif
  7162.         if(!is_absolute_path(full_filename)){
  7163.             if(F_ON(F_USE_CURRENT_DIR, ps_global))
  7164.               (void)strcpy(full_filename, filename);
  7165.             else if(VAR_OPER_DIR)
  7166.               build_path(full_filename, VAR_OPER_DIR, filename);
  7167.             else
  7168.               build_path(full_filename, ps_global->home_dir, filename);
  7169.         }
  7170.  
  7171.         break; /* Must have got an OK file name */
  7172.  
  7173.         }
  7174.  
  7175.         if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, full_filename)){
  7176.         q_status_message1(SM_ORDER, 0, 2,
  7177.             "Can't export to file outside of %s", VAR_OPER_DIR);
  7178.         goto fini;
  7179.         }
  7180.  
  7181.         /* ---- full_filename already contains the absolute path ---*/
  7182.         if(!can_access(full_filename, ACCESS_EXISTS)){
  7183.         static ESCKEY_S access_opts[] = {
  7184.             {'o', 'o', "O", "Overwrite"},
  7185.             {'a', 'a', "A", "Append"},
  7186.             {-1, 0, NULL, NULL}};
  7187.  
  7188.         rc = strlen(filename);
  7189.         sprintf(prompt,
  7190.             "File \"%s%s\" already exists.  Overwrite or append it ? ",
  7191.             (rc > 20) ? "..." : "",
  7192.             filename + ((rc > 20) ? rc - 20 : 0));
  7193.         switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global),
  7194.                      access_opts, 'a', 'x', NO_HELP, RB_NORM)){
  7195.           case 'o' :
  7196.             over = 1;
  7197.             if(unlink(full_filename) < 0){    /* BUG: breaks links */
  7198.             q_status_message2(SM_ORDER | SM_DING, 3, 5,
  7199.                       "Error deleting old %s: %s",
  7200.                       full_filename,
  7201.                       error_description(errno));
  7202.             goto fini;
  7203.             }
  7204.  
  7205.             break;
  7206.  
  7207.           case 'a' :
  7208.             over = -1;
  7209.             break;
  7210.  
  7211.           case 'x' :
  7212.           default :
  7213.             q_status_message(SM_ORDER, 0, 2, "Export cancelled");
  7214.             goto fini;
  7215.         }
  7216.         }
  7217.  
  7218.         dprint(5, (debugfile, "Opening file \"%s\" for export\n",
  7219.         full_filename));
  7220.  
  7221.         if(!(store = so_get(FileStar, full_filename, WRITE_ACCESS))){
  7222.         q_status_message2(SM_ORDER | SM_DING, 3, 4,
  7223.               "Error opening file \"%s\" for address export: %s",
  7224.               full_filename, error_description(errno));
  7225.         goto fini;
  7226.         }
  7227.         else
  7228.           gf_set_so_writec(&pc, store);
  7229.  
  7230.         start_of_append = ftell((FILE *)so_text(store));
  7231.  
  7232.         if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
  7233.         orig_errno = errno;    /* save incase things are really bad */
  7234.         failure    = 1;
  7235.         }
  7236.  
  7237.         so_give(&store);                /* release storage */
  7238.  
  7239.         if(failure){
  7240. #ifndef    DOS
  7241.         truncate(full_filename, start_of_append);
  7242. #endif
  7243.         dprint(1, (debugfile, "FAILED Export: file \"%s\" : %s\n",
  7244.                full_filename,  error_description(orig_errno)));
  7245.         q_status_message2(SM_ORDER | SM_DING, 3, 4,
  7246.               "Error exporting to \"%s\" : %s",
  7247.               filename, error_description(orig_errno));
  7248.         }
  7249.         else
  7250.           q_status_message2(SM_ORDER,0,3,
  7251.               "Addresses %s to file \"%s\"",
  7252.               over==0 ? "exported"
  7253.                   : over==1 ? "overwritten" : "appended",
  7254.               filename);
  7255.     }
  7256.     }
  7257.  
  7258. fini:
  7259.     if(addr)
  7260.       fs_give((void **)&addr);
  7261. }
  7262.  
  7263.  
  7264. /*
  7265.  * Forward an address book entry via email attachment.
  7266.  *
  7267.  * Args: cur_line     -- The current line position (in global display list)
  7268.  *             of cursor
  7269.  *       command_line -- The screen line on which to prompt
  7270.  */
  7271. void
  7272. ab_forward(ps, cur_line)
  7273.     struct pine *ps;
  7274.     long         cur_line;
  7275. {
  7276.     AddrScrn_Disp *dl;
  7277.     AdrBk_Entry   *abe;
  7278.     ENVELOPE      *outgoing = NULL;
  7279.     BODY          *pb, *body = NULL;
  7280.     PART         **pp;
  7281.     ADDRESS       *a, *adrlist = NULL;
  7282.     char          *sig, *p,
  7283.                   *init_addr = NULL,
  7284.           *addr = NULL,
  7285.                   *error = NULL,
  7286.                   *next_piece = NULL,
  7287.                   *charset = NULL,
  7288.           *tmp = NULL,
  7289.           *decoded,
  7290.            buf[MAX_ADDRESS+1];
  7291.     gf_io_t        pc;
  7292.     long           length;
  7293.     BuildTo        bldto;
  7294.     int            are_some_unqualified = 0, expand_nicks = 0, len = 0;
  7295.  
  7296.     dprint(2, (debugfile, "- ab_forward -\n"));
  7297.  
  7298.     dl  = dlist(cur_line);
  7299.     if(dl->type != ListHead && dl->type != Simple)
  7300.       return;
  7301.  
  7302.     abe = ae(cur_line);
  7303.     if(!abe){
  7304.     q_status_message(SM_ORDER, 3, 3, "Trouble accessing current entry");
  7305.     return;
  7306.     }
  7307.  
  7308.     outgoing             = mail_newenvelope();
  7309.     outgoing->message_id = generate_message_id(ps);
  7310.     outgoing->subject = cpystr("Forwarded address book entry for Pine");
  7311.  
  7312.     body                                      = mail_newbody();
  7313.     body->type                                = TYPEMULTIPART;
  7314.     /*---- The TEXT part/body ----*/
  7315.     body->contents.part                       = mail_newbody_part();
  7316.     body->contents.part->body.type            = TYPETEXT;
  7317.     /*--- Allocate an object for the body ---*/
  7318.     if(body->contents.part->body.contents.binary =
  7319.                 (void *)so_get(PicoText, NULL, EDIT_ACCESS)){
  7320.     pp = &(body->contents.part->next);
  7321.     if(sig = get_signature()){
  7322.         if(*sig)
  7323.           so_puts((STORE_S *)body->contents.part->body.contents.binary,sig);
  7324.  
  7325.         fs_give((void **)&sig);
  7326.     }
  7327.  
  7328.     so_puts((STORE_S *)body->contents.part->body.contents.binary, "\n  [ Attached to this message is an entry from the sender's Pine address     ]\n  [ book.  To add it to your Pine address book, use the \"TakeAddr\" command. ]\n");
  7329.     }
  7330.     else{
  7331.     q_status_message(SM_ORDER | SM_DING, 3, 4,
  7332.              "Problem creating space for message text");
  7333.     goto bomb;
  7334.     }
  7335.  
  7336.  
  7337.     /*---- create the attachment, and write abook entry into it ----*/
  7338.     *pp             = mail_newbody_part();
  7339.     pb              = &((*pp)->body);
  7340.     pb->type        = TYPEAPPLICATION;
  7341.     pb->encoding    = ENCOTHER;  /* let data decide */
  7342.     pb->id          = generate_message_id(ps);
  7343.     pb->subtype     = cpystr("DIRECTORY");
  7344.     pb->description = cpystr("Pine addressbook entry");
  7345.     pb->parameter   = mail_newbody_parameter();
  7346.     pb->parameter->attribute = cpystr("profile");
  7347.     pb->parameter->value     = cpystr("X-Email-Abook-Entry");
  7348.     pb->contents.msg.env  = NULL;
  7349.     pb->contents.msg.body = NULL;
  7350.  
  7351.     if(pb->contents.binary = (void *)so_get(CharStar, NULL, EDIT_ACCESS)){
  7352.     gf_set_so_writec(&pc, (STORE_S *)pb->contents.binary);
  7353.  
  7354.     if(abe->nickname && abe->nickname[0]){
  7355.         sprintf(tmp_20k_buf, "X-Nickname: %s\r\n", abe->nickname);
  7356.         gf_puts(tmp_20k_buf, pc);
  7357.     }
  7358.  
  7359.     if(abe->fullname && abe->fullname[0]){
  7360.         decoded
  7361.           = (char *)rfc1522_decode((unsigned char *)(tmp_20k_buf+10000),
  7362.          abe->fullname, &charset);
  7363.         sprintf(tmp_20k_buf, "CN%s%s: %s\r\n",
  7364.          (charset && *charset) ? ";charset=" : "",
  7365.          (charset && *charset) ? charset : "",
  7366.          decoded);
  7367.         gf_puts(tmp_20k_buf, pc);
  7368.         if(charset)
  7369.           fs_give((void **)&charset);
  7370.     }
  7371.  
  7372.     if(abe->fcc && abe->fcc[0]){
  7373.         sprintf(tmp_20k_buf, "X-Fcc: %s\r\n", abe->fcc);
  7374.         gf_puts(tmp_20k_buf, pc);
  7375.     }
  7376.  
  7377.     /*
  7378.      * Fold long comment lines.  Don't even worry about folding
  7379.      * the other types of lines.
  7380.      */
  7381. #define FOLDHERE 72
  7382.     next_piece = abe->extra;
  7383.     if(next_piece && *next_piece){
  7384.         tmp = (char *)fs_get(strlen(next_piece) + 1);
  7385.         decoded = (char *)rfc1522_decode((unsigned char *)tmp,
  7386.               next_piece, &charset);
  7387.         sprintf(tmp_20k_buf, "Misc%s%s:",
  7388.          (charset && *charset) ? ";charset=" : "",
  7389.          (charset && *charset) ? charset : "");
  7390.  
  7391.         len = strlen(tmp_20k_buf);
  7392.         next_piece = decoded;
  7393.         gf_puts(tmp_20k_buf, pc);
  7394.         if(charset)
  7395.           fs_give((void **)&charset);
  7396.     }
  7397.     
  7398.     while(next_piece && *next_piece){
  7399.         if(strlen(next_piece) + len < FOLDHERE){
  7400.         sprintf(tmp_20k_buf, " %s\r\n", next_piece);
  7401.         gf_puts(tmp_20k_buf, pc);
  7402.         break;
  7403.         }
  7404.         else{ /* fold it */
  7405.         char save_char;
  7406.         int  i, starting_point, higher, lower, winner = -1;
  7407.  
  7408.         starting_point = FOLDHERE - len;
  7409.         /* find a good folding spot */
  7410.         for(i = 0; i < 40 && winner == -1; i++){
  7411.             higher = starting_point + i;
  7412.             lower  = starting_point - 1 - i;
  7413.             if(!next_piece[higher]
  7414.                || isspace((unsigned char)next_piece[higher]))
  7415.               winner = higher;
  7416.  
  7417.             if(!next_piece[lower]
  7418.                || isspace((unsigned char)next_piece[lower]))
  7419.               winner = lower;
  7420.         }
  7421.  
  7422.         if(winner == -1) /* if no good folding spot, fold at FOLDHERE */
  7423.           winner = starting_point;
  7424.         
  7425.         save_char = next_piece[winner];
  7426.         next_piece[winner] = '\0';
  7427.         sprintf(tmp_20k_buf, " %s\r\n", next_piece);
  7428.         gf_puts(tmp_20k_buf, pc);
  7429.         next_piece[winner] = save_char;
  7430.         next_piece += winner;
  7431.         if(isspace((unsigned char)save_char))
  7432.           next_piece++;
  7433.         }
  7434.  
  7435.         len = 0;
  7436.     }
  7437.  
  7438.     if(tmp)
  7439.       fs_give((void **)&tmp);
  7440.  
  7441.     /*
  7442.      * Search through the addresses to see if there are any
  7443.      * that are unqualified, and so would be different if
  7444.      * expanded.
  7445.      */
  7446.     if(abe->tag == Single && abe->addr.addr && abe->addr.addr[0]){
  7447.         if(!strindex(abe->addr.addr, '@'))
  7448.           are_some_unqualified++;
  7449.     }
  7450.     else{
  7451.         char **ll;
  7452.  
  7453.         for(ll = abe->addr.list; ll && *ll; ll++){
  7454.         if(!strindex(*ll, '@')){
  7455.             are_some_unqualified++;
  7456.             break;
  7457.         }
  7458.         }
  7459.     }
  7460.  
  7461.     if(are_some_unqualified){
  7462.         switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward, 0, 0)){
  7463.           case 'x':
  7464.         q_status_message(SM_ORDER, 0, 4, "Forward cancelled");
  7465.         goto bomb;
  7466.         break;
  7467.         
  7468.           case 'y':
  7469.         expand_nicks = 1;
  7470.         break;
  7471.         
  7472.           case 'n':
  7473.         expand_nicks = 0;
  7474.         break;
  7475.         }
  7476.     }
  7477.  
  7478.     /* expand nicknames and fully-qualify unqualified names */
  7479.     if(expand_nicks){
  7480.         if(abe->tag == Single && abe->addr.addr && abe->addr.addr[0])
  7481.           init_addr = cpystr(abe->addr.addr);
  7482.         else{
  7483.         char **ll;
  7484.  
  7485.         /* figure out how large a string we need to allocate */
  7486.         length = 0L;
  7487.         for(ll = abe->addr.list; ll && *ll; ll++)
  7488.           length += (strlen(*ll) + 2);
  7489.  
  7490.         if(length)
  7491.           length -= 2L;
  7492.         
  7493.         init_addr = (char *)fs_get((size_t)(length+1L) * sizeof(char));
  7494.         p = init_addr;
  7495.         
  7496.         for(ll = abe->addr.list; ll && *ll; ll++){
  7497.             sstrcpy(&p, *ll);
  7498.             if(*(ll+1))
  7499.               sstrcpy(&p, ", ");
  7500.         }
  7501.         }
  7502.  
  7503.         bldto.type    = Str;
  7504.         bldto.arg.str = init_addr; 
  7505.         our_build_address(bldto, &addr, &error, NULL, 0, 0);
  7506.         if(error){
  7507.         q_status_message1(SM_ORDER, 3, 4, "%s", error);
  7508.         fs_give((void **)&error);
  7509.         goto bomb;
  7510.         }
  7511.  
  7512.         if(addr)
  7513.           rfc822_parse_adrlist(&adrlist, addr, ps->maildomain);
  7514.         
  7515.         for(a = adrlist; a; a = a->next){
  7516.         decoded
  7517.           = (char *)rfc1522_decode((unsigned char *)(tmp_20k_buf+10000),
  7518.              addr_string(a, buf), &charset);
  7519.         sprintf(tmp_20k_buf, "Email%s%s: %s\r\n",
  7520.              (charset && *charset) ? ";charset=" : "",
  7521.              (charset && *charset) ? charset : "",
  7522.              decoded);
  7523.         gf_puts(tmp_20k_buf, pc);
  7524.         if(charset)
  7525.           fs_give((void **)&charset);
  7526.         }
  7527.     }
  7528.     else{ /* don't expand or qualify */
  7529.         if(abe->tag == Single && abe->addr.addr && abe->addr.addr[0]){
  7530.         decoded
  7531.           = (char *)rfc1522_decode((unsigned char *)(tmp_20k_buf+10000),
  7532.              abe->addr.addr, &charset);
  7533.         sprintf(tmp_20k_buf, "Email%s%s: %s\r\n",
  7534.              (charset && *charset) ? ";charset=" : "",
  7535.              (charset && *charset) ? charset : "",
  7536.              decoded);
  7537.         gf_puts(tmp_20k_buf, pc);
  7538.         if(charset)
  7539.           fs_give((void **)&charset);
  7540.         }
  7541.         else{
  7542.         char **ll;
  7543.  
  7544.         for(ll = abe->addr.list; ll && *ll; ll++){
  7545.             decoded = (char *)rfc1522_decode(
  7546.                     (unsigned char *)(tmp_20k_buf+10000),
  7547.                     *ll, &charset);
  7548.             sprintf(tmp_20k_buf, "Email%s%s: %s\r\n",
  7549.              (charset && *charset) ? ";charset=" : "",
  7550.              (charset && *charset) ? charset : "",
  7551.              decoded);
  7552.             gf_puts(tmp_20k_buf, pc);
  7553.             if(charset)
  7554.               fs_give((void **)&charset);
  7555.         }
  7556.         }
  7557.     }
  7558.  
  7559.     /* This sets parameter charset, if necessary, and encoding */
  7560.     set_mime_type_by_grope(pb);
  7561.     pb->size.bytes =
  7562.         strlen((char *)so_text((STORE_S *)pb->contents.binary));
  7563.     }
  7564.     else{
  7565.     q_status_message(SM_ORDER | SM_DING, 3, 4,
  7566.              "Problem creating space for message text");
  7567.     goto bomb;
  7568.     }
  7569.  
  7570.     pine_send(outgoing, &body, "FORWARDING ADDRESS BOOK ENTRY", NULL,
  7571.           NULL, NULL, NULL, NULL, NULL, 0);
  7572.     
  7573.     ps->mangled_screen = 1;
  7574.  
  7575. bomb:
  7576.     if(outgoing)
  7577.       mail_free_envelope(&outgoing);
  7578.     if(body)
  7579.       pine_free_body(&body);
  7580.     if(addr)
  7581.       fs_give((void **)&addr);
  7582.     if(init_addr)
  7583.       fs_give((void **)&init_addr);
  7584.     if(adrlist)
  7585.       mail_free_address(&adrlist);
  7586. }
  7587.  
  7588.  
  7589. /*
  7590.  * Go to folder.
  7591.  *
  7592.  *       command_line -- The screen line on which to prompt
  7593.  */
  7594. void
  7595. ab_goto_folder(command_line)
  7596.     int command_line;
  7597. {
  7598.     char *go_folder;
  7599.     CONTEXT_S *tc;
  7600.  
  7601.     dprint(2, (debugfile, "- ab_goto_folder -\n"));
  7602.  
  7603.     tc = (ps_global->context_last
  7604.           && !(ps_global->context_current->type & FTYPE_BBOARD)) 
  7605.            ? ps_global->context_last : ps_global->context_current;
  7606.  
  7607.     go_folder = broach_folder(command_line, 1, &tc);
  7608.  
  7609. #if defined(DOS) && !defined(_WINDOWS)
  7610.     if(go_folder && *go_folder == '{' && coreleft() < 20000){
  7611.     q_status_message(SM_ORDER | SM_DING, 3, 4,
  7612.              "Not enough memory to open IMAP folder");
  7613.     go_folder = NULL;
  7614.     }
  7615. #endif /* !DOS */
  7616.  
  7617.     if(go_folder != NULL)
  7618.       visit_folder(ps_global, go_folder, tc);
  7619. }
  7620.  
  7621.  
  7622. /*
  7623.  * Execute whereis command.
  7624.  *
  7625.  * Returns value of the new top entry, or NO_LINE if cancelled.
  7626.  */
  7627. long
  7628. ab_whereis(warped, command_line)
  7629.     int *warped;
  7630.     int  command_line;
  7631. {
  7632.     int rc, wrapped = 0;
  7633.     long new_top_ent, new_line;
  7634.  
  7635.     dprint(2, (debugfile, "- ab_whereis -\n"));
  7636.  
  7637.     rc = search_book(as.top_ent+as.cur_row, command_line,
  7638.             &new_line, &wrapped, warped);
  7639.  
  7640.     new_top_ent = NO_LINE;
  7641.  
  7642.     if(rc == -2)
  7643.       cancel_warning(NO_DING, "search");
  7644.  
  7645.     else if(rc == -1)
  7646.       q_status_message(SM_ORDER, 0, 4, "Word not found");
  7647.  
  7648.     else if(rc == 0){  /* search succeeded */
  7649.  
  7650.     if(wrapped)
  7651.       q_status_message(SM_INFO, 0, 2, "Search wrapped to beginning");
  7652.  
  7653.     /* know match is on the same page */
  7654.     if(!*warped &&
  7655.         new_line >= as.top_ent &&
  7656.         new_line < as.top_ent+as.l_p_page)
  7657.         new_top_ent = as.top_ent;
  7658.     /* don't know whether it is or not, reset top_ent */
  7659.     else
  7660.       new_top_ent = first_line(new_line - as.l_p_page/2);
  7661.  
  7662.     as.cur_row  = new_line - new_top_ent;
  7663.     }
  7664.  
  7665.     return(new_top_ent);
  7666. }
  7667.  
  7668.  
  7669. /*
  7670.  * Print out the display list.
  7671.  */
  7672. void
  7673. ab_print()
  7674. {
  7675.     AddrScrn_Disp *dl; 
  7676.     long lineno;
  7677.     AdrBk_Entry *abe;
  7678.     long save_line;
  7679.     DL_CACHE_S dlc_buf, *match_dlc;
  7680.     char *fullname, *addr, *lm; 
  7681.  
  7682.     if(open_printer("address books ") == 0){
  7683.  
  7684.     save_line = as.top_ent + as.cur_row;
  7685.     match_dlc = get_dlc(save_line);
  7686.     dlc_buf   = *match_dlc;
  7687.     match_dlc = &dlc_buf;
  7688.  
  7689.     warp_to_beginning();
  7690.     lineno = 0L;
  7691.  
  7692.     for(dl = dlist(lineno);
  7693.         dl->type != End;
  7694.         dl = dlist(++lineno)){
  7695.         switch(dl->type){
  7696.           case Simple:
  7697.         abe = ae(lineno);
  7698.         fullname = abe->fullname ? abe->fullname : "";
  7699.         addr = (abe->tag == Single && abe->addr.addr)
  7700.                 ? abe->addr.addr : "";
  7701.         print_text3("%-10.10s %-35.35s %s\n",
  7702.             abe->nickname,
  7703.             (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  7704.                         fullname, NULL),
  7705.             addr);
  7706.         break;
  7707.  
  7708.           case ListHead:
  7709.         abe = ae(lineno);
  7710.         fullname = abe->fullname ? abe->fullname : "";
  7711.         print_text3("%-10.10s %-35.35s %s\n",
  7712.             abe->nickname,
  7713.             (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  7714.                         fullname, NULL),
  7715.             DISTLIST);
  7716.         break;
  7717.  
  7718.           case ListEnt:
  7719.         lm = listmem(lineno) ? listmem(lineno) : "";
  7720.     print_text1("                                               %s\n",
  7721.                 (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  7722.                             lm, NULL));
  7723.         break;
  7724.  
  7725.           case ClickHere:
  7726.         print_text1("%s\n", CLICKHERE);
  7727.         break;
  7728.  
  7729.           case Empty:
  7730.         print_text1("%s\n", EMPTY);
  7731.         break;
  7732.  
  7733.           case ListEmpty:
  7734.     print_text1("                                               %s\n", EMPTY);
  7735.         break;
  7736.  
  7737.           case Text:
  7738.           case Title:
  7739.         print_text1("%s\n", dl->txt);
  7740.         break;
  7741.  
  7742.           case ListClickHere:
  7743.         break;
  7744.         }
  7745.     }
  7746.  
  7747.     close_printer();
  7748.  
  7749.     /*
  7750.      * jump cache back to where we started so that the next
  7751.      * request won't cause us to page through the whole thing
  7752.      */
  7753.     warp_to_dlc(match_dlc, save_line);
  7754.     }
  7755. }
  7756.  
  7757.  
  7758. /*
  7759.  * recalculate display parameters for window size change
  7760.  */
  7761. void
  7762. ab_resize()
  7763. {
  7764.     long new_line;
  7765.     int  old_l_p_p;
  7766.     DL_CACHE_S dlc_buf, *dlc_restart;
  7767.  
  7768.     old_l_p_p   = as.l_p_page;
  7769.     as.l_p_page = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)
  7770.                            - HEADER_ROWS(ps_global);
  7771.  
  7772.     dprint(9, (debugfile, "- ab_resize -\n    l_p_p was %d, now %d\n",
  7773.     old_l_p_p, as.l_p_page));
  7774.  
  7775.     if(as.l_p_page <= 0)
  7776.       return;
  7777.  
  7778.     new_line       = as.top_ent + as.cur_row;
  7779.     as.top_ent     = first_line(new_line - as.l_p_page/2);
  7780.     as.cur_row     = new_line - as.top_ent;
  7781.     as.old_cur_row = as.cur_row;
  7782.  
  7783.     /* need this to re-initialize Text and Title lines in display */
  7784.     /* get the old current line (which may be the wrong width) */
  7785.     dlc_restart = get_dlc(new_line);
  7786.     /* flush it from cache */
  7787.     flush_dlc_from_cache(dlc_restart);
  7788.     /* re-get it (should be right now) */
  7789.     dlc_restart = get_dlc(new_line);
  7790.     /* copy it to local storage */
  7791.     dlc_buf = *dlc_restart;
  7792.     dlc_restart = &dlc_buf;
  7793.     /* flush everything from cache and add that one line back in */
  7794.     warp_to_dlc(dlc_restart, new_line);
  7795. }
  7796.  
  7797.  
  7798. /*
  7799.  * Returns 0 if we know for sure that there are no
  7800.  * addresses available in any of the addressbooks.
  7801.  *
  7802.  * Easiest would be to start at 0 and go through the addrbook, but that will
  7803.  * be very slow for big addrbooks if we're not close to 0 already.  Instead,
  7804.  * starting_hint is a hint at a good place to start looking.
  7805.  */
  7806. int
  7807. any_addrs_avail(starting_hint)
  7808.     long starting_hint;
  7809. {
  7810.     register AddrScrn_Disp *dl;
  7811.     long lineno;
  7812.  
  7813.     /*
  7814.      * Look from lineno backwards first, in hopes of finding it in cache.
  7815.      */
  7816.     lineno = starting_hint;
  7817.     for(dl=dlist(lineno);
  7818.     dl->type != Beginning;
  7819.     dl = dlist(--lineno)){
  7820.     switch(dl->type){
  7821.       case Simple:
  7822.       case ListEnt:
  7823.       case ListHead:
  7824.       case ClickHere:
  7825.       case ListClickHere:
  7826.         return 1;
  7827.     }
  7828.     }
  7829.  
  7830.     /* search from here forward if we still don't know */
  7831.     lineno = starting_hint;
  7832.     for(dl=dlist(lineno);
  7833.     dl->type != End;
  7834.     dl = dlist(++lineno)){
  7835.     switch(dl->type){
  7836.       case Simple:
  7837.       case ListEnt:
  7838.       case ListHead:
  7839.       case ClickHere:
  7840.       case ListClickHere:
  7841.         return 1;
  7842.     }
  7843.     }
  7844.  
  7845.     return 0;
  7846. }
  7847.  
  7848.  
  7849. /*
  7850.  * Returns 1 if this line is a clickable line.
  7851.  */
  7852. int
  7853. entry_is_clickable(lineno)
  7854.     long lineno;
  7855. {
  7856.     register AddrScrn_Disp *dl;
  7857.  
  7858.     if((dl = dlist(lineno)) &&
  7859.             (dl->type == ClickHere || dl->type == ListClickHere))
  7860.       return 1;
  7861.  
  7862.     return 0;
  7863. }
  7864.  
  7865.  
  7866. /*
  7867.  * Returns 1 if an address or list is selected.
  7868.  */
  7869. int
  7870. is_addr(lineno)
  7871.     long lineno;
  7872. {
  7873.     register AddrScrn_Disp *dl;
  7874.  
  7875.     if((dl = dlist(lineno)) && (dl->type == ListHead ||
  7876.                     dl->type == ListEnt  ||
  7877.                     dl->type == Simple))
  7878.     return 1;
  7879.  
  7880.     return 0;
  7881. }
  7882.  
  7883.  
  7884. /*
  7885.  * Returns 1 if type of line is Empty.
  7886.  */
  7887. int
  7888. is_empty(lineno)
  7889.     long lineno;
  7890. {
  7891.     register AddrScrn_Disp *dl;
  7892.  
  7893.     if((dl = dlist(lineno)) && (dl->type == Empty || dl->type == ListEmpty))
  7894.       return 1;
  7895.  
  7896.     return 0;
  7897. }
  7898.  
  7899.  
  7900. /*
  7901.  * Returns 1 if this line is of a type that can have a cursor on it.
  7902.  */
  7903. int
  7904. line_is_selectable(lineno)
  7905.     long lineno;
  7906. {
  7907.     register AddrScrn_Disp *dl;
  7908.  
  7909.     if((dl = dlist(lineno)) && (dl->type == Text      ||
  7910.                 dl->type == Title     ||
  7911.                 dl->type == ListEmpty ||
  7912.                 dl->type == Beginning ||
  7913.                 dl->type == End))
  7914.     return 0;
  7915.  
  7916.     return 1;
  7917. }
  7918.  
  7919.  
  7920. /*
  7921.  * Find the first selectable line greater than or equal to line.  That is,
  7922.  * the first line the cursor is allowed to start on.
  7923.  * (If there are none >= line, it will find the highest one.)
  7924.  *
  7925.  * Returns the line number of the found line or NO_LINE if there isn't one.
  7926.  */
  7927. long
  7928. first_selectable_line(line)
  7929.     long line;
  7930. {
  7931.     long lineno;
  7932.     register PerAddrBook *pab;
  7933.     int i;
  7934.  
  7935.     /* skip past non-selectable lines */
  7936.     for(lineno=line;
  7937.     !line_is_selectable(lineno) && dlist(lineno)->type != End;
  7938.     lineno++)
  7939.     ;/* do nothing */
  7940.  
  7941.     if(line_is_selectable(lineno))
  7942.       return(lineno);
  7943.  
  7944.     /*
  7945.      * There were no selectable lines from lineno on down.  Trying looking
  7946.      * back up the list.
  7947.      */
  7948.     for(lineno=line-1;
  7949.     !line_is_selectable(lineno) && dlist(lineno)->type != Beginning;
  7950.     lineno--)
  7951.     ;/* do nothing */
  7952.  
  7953.     if(line_is_selectable(lineno))
  7954.       return(lineno);
  7955.  
  7956.     /*
  7957.      * No selectable lines at all.
  7958.      * If some of the addrbooks are still not displayed, it is too
  7959.      * early to set the no_op_possbl flag.  Or, if some of the addrbooks
  7960.      * are empty but writable, then we should not set it either.
  7961.      */
  7962.     for(i = 0; i < as.n_addrbk; i++){
  7963.     pab = &as.adrbks[i];
  7964.     if(pab->ostatus != Open && pab->ostatus != HalfOpen)
  7965.       return NO_LINE;
  7966.  
  7967.     if(pab->access == ReadWrite && adrbk_count(pab->address_book) == 0)
  7968.       return NO_LINE;
  7969.     }
  7970.  
  7971.     as.no_op_possbl++;
  7972.     return NO_LINE;
  7973. }
  7974.  
  7975.  
  7976. /*
  7977.  * Find the first line greater than or equal to line.  (Any line, not
  7978.  * necessarily selectable.)
  7979.  *
  7980.  * Returns the line number of the found line or NO_LINE if there is none.
  7981.  *
  7982.  * Warning:  This just starts at the passed in line and goes forward until
  7983.  * it runs into a line that isn't a Beginning line.  If the line passed in
  7984.  * is not in the dlc cache, it will have no way to know when it gets to the
  7985.  * real beginning.
  7986.  */
  7987. long
  7988. first_line(line)
  7989.     long line;
  7990. {
  7991.     long lineno;
  7992.     register PerAddrBook *pab;
  7993.     int i;
  7994.  
  7995.     for(lineno=line;
  7996.        dlist(lineno)->type == Beginning;
  7997.        lineno++)
  7998.     ;/* do nothing */
  7999.  
  8000.     if(dlist(lineno)->type != End)
  8001.       return(lineno);
  8002.     else{
  8003.     for(i = 0; i < as.n_addrbk; i++){
  8004.         pab = &as.adrbks[i];
  8005.         if(pab->ostatus != Open && pab->ostatus != HalfOpen)
  8006.           return NO_LINE;
  8007.     }
  8008.  
  8009.     as.no_op_possbl++;
  8010.     return(NO_LINE);
  8011.     }
  8012. }
  8013.  
  8014.  
  8015. /*
  8016.  * Find the line and field number of the next selectable line, keeping the
  8017.  * field about the same.
  8018.  *
  8019.  * Args: cur_line     -- The current line position (in global display list)
  8020.  *             of cursor
  8021.  *       new_line     -- Return value: new line position
  8022.  *
  8023.  * Result: The new line number is set.
  8024.  *       The value 1 is returned if OK or 0 if there is no next line.
  8025.  */
  8026. int
  8027. next_selectable_line(cur_line, new_line)
  8028.     long  cur_line;
  8029.     long *new_line;
  8030. {
  8031.     /* skip over non-selectable lines */
  8032.     for(cur_line++;
  8033.     !line_is_selectable(cur_line) && dlist(cur_line)->type != End;
  8034.     cur_line++)
  8035.     ;/* do nothing */
  8036.  
  8037.     if(dlist(cur_line)->type == End)
  8038.       return 0;
  8039.  
  8040.     *new_line = cur_line;
  8041.     return 1;
  8042. }
  8043.  
  8044.  
  8045. /*
  8046.  * Find the line and field number of the previous selectable line, keeping the
  8047.  * field about the same.
  8048.  *
  8049.  * Args: cur_line     -- The current line position (in global display list)
  8050.  *             of cursor
  8051.  *       new_line     -- Return value: new line position
  8052.  *
  8053.  * Result: The new line number is set.
  8054.  *       The value 1 is returned if OK or 0 if there is no previous line.
  8055.  */
  8056. int
  8057. prev_selectable_line(cur_line, new_line)
  8058.     long  cur_line;
  8059.     long *new_line;
  8060. {
  8061.     /* skip backwards over non-selectable lines */
  8062.     for(cur_line--;
  8063.     !line_is_selectable(cur_line) && dlist(cur_line)->type != Beginning;
  8064.     cur_line--)
  8065.     ;/* do nothing */
  8066.  
  8067.     if(dlist(cur_line)->type == Beginning)
  8068.       return 0;
  8069.  
  8070.     *new_line = cur_line;
  8071.  
  8072.     return 1;
  8073. }
  8074.  
  8075.  
  8076. /*
  8077.  * Delete an entry from the address book
  8078.  *
  8079.  * Args: abook        -- The addrbook handle into access library
  8080.  *       command_line -- The screen line on which to prompt
  8081.  *       cur_line     -- The entry number in the display list
  8082.  *       warped       -- We warped to a new part of the addrbook
  8083.  *
  8084.  * Result: returns 1 if an entry was deleted, 0 if not.
  8085.  *
  8086.  * The main routine above knows what to repaint because it's always the
  8087.  * current entry that's deleted.  Here confirmation is asked of the user
  8088.  * and the appropriate adrbklib functions are called.
  8089.  */
  8090. int
  8091. addr_book_delete(abook, command_line, cur_line, warped)
  8092.     AdrBk *abook;
  8093.     int    command_line;
  8094.     long   cur_line;
  8095.     int   *warped;
  8096. {
  8097.     char   ch, *cmd, *dname;
  8098.     char   prompt[40+MAX_ADDRESS+1]; /* 40 is len of string constants below */
  8099.     int    rc = command_line; /* nuke warning about command_line unused */
  8100.     register AddrScrn_Disp *dl;
  8101.     AdrBk_Entry     *abe;
  8102.     DL_CACHE_S      *dlc_to_flush;
  8103.  
  8104.     dprint(2, (debugfile, "- addr_book_delete -\n"));
  8105.  
  8106.     if(warped)
  8107.       *warped = 0;
  8108.  
  8109.     dl  = dlist(cur_line);
  8110.     abe = adrbk_get_ae(abook, (a_c_arg_t)dl->elnum, Normal);
  8111.  
  8112.     switch(dl->type){
  8113.       case Simple:
  8114.     dname =    (abe->fullname && abe->fullname[0])
  8115.             ? (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  8116.                             abe->fullname, NULL)
  8117.             : abe->nickname ? abe->nickname : "";
  8118.         cmd   = "Really delete \"%.50s\"";
  8119.         break;
  8120.  
  8121.       case ListHead:
  8122.     dname =    (abe->fullname && abe->fullname[0])
  8123.             ? (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  8124.                             abe->fullname, NULL)
  8125.             : abe->nickname ? abe->nickname : "";
  8126.     cmd   = "Really delete ENTIRE list \"%.50s\"";
  8127.         break;
  8128.  
  8129.       case ListEnt:
  8130.         dname = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  8131.                         listmem_from_dl(abook, dl), NULL);
  8132.     cmd   = "Really delete \"%.100s\" from list";
  8133.         break;
  8134.     } 
  8135.  
  8136.     dname = dname ? dname : "";
  8137.     cmd   = cmd   ? cmd   : "";
  8138.  
  8139.     sprintf(prompt, cmd, dname);
  8140.     ch = want_to(prompt, 'n', 'n', NO_HELP, 0, 0);
  8141.     if(ch == 'y'){
  8142.     dlc_to_flush = get_dlc(cur_line);
  8143.     if(dl->type == Simple || dl->type == ListHead){
  8144.         /*--- Kill a single entry or an entire list ---*/
  8145.             rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 1);
  8146.     }
  8147.     else if(listmem_count_from_abe(abe) > 2){
  8148.             /*---- Kill an entry out of a list ----*/
  8149.             rc = adrbk_listdel(abook, (a_c_arg_t)dl->elnum,
  8150.             listmem_from_dl(abook, dl));
  8151.     }
  8152.     else{
  8153.         char *nick, *full, *addr, *fcc, *comment;
  8154.         adrbk_cntr_t new_entry_num = NO_NEXT;
  8155.  
  8156.         /*---- Convert a List to a Single entry ----*/
  8157.  
  8158.         /* Save old info to be transferred */
  8159.         nick    = cpystr(abe->nickname);
  8160.         full    = cpystr(abe->fullname);
  8161.         fcc     = cpystr(abe->fcc);
  8162.         comment = cpystr(abe->extra);
  8163.         if(listmem_count_from_abe(abe) == 2)
  8164.           addr = cpystr(abe->addr.list[1 - dl->l_offset]);
  8165.         else
  8166.           addr = cpystr("");
  8167.  
  8168.             rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 0);
  8169.         if(rc == 0)
  8170.           adrbk_add(abook,
  8171.             NO_NEXT,
  8172.             nick,
  8173.             full,
  8174.             addr,
  8175.             fcc,
  8176.             comment,
  8177.             Single,
  8178.             &new_entry_num,
  8179.             NULL);
  8180.  
  8181.         fs_give((void **)&nick);
  8182.         fs_give((void **)&full);
  8183.         fs_give((void **)&fcc);
  8184.         fs_give((void **)&comment);
  8185.         fs_give((void **)&addr);
  8186.  
  8187.         if(rc == 0){
  8188.         DL_CACHE_S dlc_restart;
  8189.  
  8190.         dlc_restart.adrbk_num = as.cur;
  8191.         dlc_restart.dlcelnum  = new_entry_num;
  8192.         dlc_restart.type = DlcSimple;
  8193.         warp_to_dlc(&dlc_restart, 0L);
  8194.         *warped = 1;
  8195.         return 1;
  8196.         }
  8197.     }
  8198.  
  8199.     if(rc == 0){
  8200.         q_status_message(SM_ORDER, 0, 3,
  8201.         "Entry deleted, address book updated");
  8202.             dprint(2, (debugfile, "abook: Entry %s\n",
  8203.         (dl->type == Simple || dl->type == ListHead) ? "deleted"
  8204.                                  : "modified"));
  8205.         /*
  8206.          * Remove deleted line and everything after it from
  8207.          * the dlc cache.  Next time we try to access those lines they
  8208.          * will get filled in with the right info.
  8209.          */
  8210.         flush_dlc_from_cache(dlc_to_flush);
  8211.             return 1;
  8212.         }
  8213.     else{
  8214.         PerAddrBook     *pab;
  8215.  
  8216.         if(rc != -5)
  8217.               q_status_message1(SM_ORDER | SM_DING, 3, 5,
  8218.                   "Error updating address book: %s",
  8219.             error_description(errno));
  8220.         pab = &as.adrbks[as.cur];
  8221.             dprint(1, (debugfile, "Error deleting entry from %s (%s): %s\n",
  8222.         pab->nickname, pab->filename, error_description(errno)));
  8223.         }
  8224.  
  8225.     return 0;
  8226.     }
  8227.     else{
  8228.     q_status_message(SM_INFO, 0, 2, "Entry not deleted");
  8229.     return 0;
  8230.     }
  8231. }
  8232.  
  8233.  
  8234. /*
  8235.  * Edit a nickname field.
  8236.  *
  8237.  * Args: abook     -- the addressbook handle
  8238.  *       dl        -- display list line (NULL if new entry)
  8239.  *    command_line -- line to prompt on
  8240.  *       orig      -- nickname to edit
  8241.  *       prompt    -- prompt
  8242.  *      this_help  -- help
  8243.  * return_existing -- changes the behavior when a user types in a nickname
  8244.  *                    which already exists in this abook.  If not set, it
  8245.  *                    will just keep looping until the user changes; if set,
  8246.  *                    it will return -8 to the caller and orig will be set
  8247.  *                    to the matching nickname.
  8248.  *
  8249.  * Returns: -10 to cancel
  8250.  *          -9  no change
  8251.  *          -7  only case of nickname changed (only happens if dl set)
  8252.  *          -8  existing nickname chosen (only happens if return_existing set)
  8253.  *           0  new value copied into orig
  8254.  */
  8255. int
  8256. edit_nickname(abook, dl, command_line, orig, prompt, this_help,
  8257.         return_existing, takeaddr)
  8258.     AdrBk         *abook;
  8259.     AddrScrn_Disp *dl;
  8260.     int            command_line;
  8261.     char          *orig,
  8262.           *prompt;
  8263.     HelpType       this_help;
  8264.     int            return_existing,
  8265.            takeaddr;
  8266. {
  8267.     char         edit_buf[MAX_NICKNAME + 1];
  8268.     HelpType     help;
  8269.     int          rc;
  8270.     AdrBk_Entry *check, *passed_in_ae;
  8271.     ESCKEY_S     ekey[2];
  8272.     SAVE_STATE_S state;  /* For saving state of addrbooks temporarily */
  8273.     char        *error = NULL;
  8274.  
  8275.     ekey[0].ch    = ctrl('T');
  8276.     ekey[0].rval  = 2;
  8277.     ekey[0].name  = "^T";
  8278.     ekey[0].label = "To AddrBk";
  8279.  
  8280.     ekey[1].ch    = -1;
  8281.  
  8282.     edit_buf[MAX_NICKNAME] = '\0';
  8283.     strncpy(edit_buf, orig, MAX_NICKNAME);
  8284.     if(dl)
  8285.       passed_in_ae = adrbk_get_ae(abook, (a_c_arg_t)dl->elnum, Lock);
  8286.     else
  8287.       passed_in_ae = (AdrBk_Entry *)NULL;
  8288.  
  8289.     help  = NO_HELP;
  8290.     rc    = 0;
  8291.     check = NULL;
  8292.     do{
  8293.     if(error){
  8294.         q_status_message(SM_ORDER, 3, 4, error);
  8295.         fs_give((void **)&error);
  8296.     }
  8297.  
  8298.     /* display a message because adrbk_lookup_by_nick returned positive */
  8299.     if(check){
  8300.         if(return_existing){
  8301.         strcpy(orig, edit_buf);
  8302.         if(passed_in_ae)
  8303.           (void)adrbk_get_ae(abook, (a_c_arg_t)dl->elnum, Unlock);
  8304.         return -8;
  8305.         }
  8306.  
  8307.             q_status_message1(SM_ORDER, 0, 4,
  8308.             "Already an entry with nickname \"%s\"", edit_buf);
  8309.     }
  8310.  
  8311.     if(rc == 3)
  8312.           help = (help == NO_HELP ? this_help : NO_HELP);
  8313.  
  8314.     rc = optionally_enter(edit_buf, command_line, 0, MAX_NICKNAME, 1,
  8315.               0, prompt, ekey, help, 0);
  8316.  
  8317.     if(rc == 1)  /* ^C */
  8318.       break;
  8319.  
  8320.     if(rc == 2){ /* ^T */
  8321.         void (*redraw) () = ps_global->redrawer;
  8322.         char *returned_nickname;
  8323.  
  8324.         push_titlebar_state();
  8325.         save_state(&state);
  8326.         if(takeaddr)
  8327.           returned_nickname = addr_book_takeaddr();
  8328.         else
  8329.           returned_nickname = addr_book_selnick();
  8330.  
  8331.         restore_state(&state);
  8332.         if(returned_nickname){
  8333.         strncpy(edit_buf, returned_nickname, MAX_NICKNAME);
  8334.         fs_give((void **)&returned_nickname);
  8335.         }
  8336.  
  8337.         ClearScreen();
  8338.         pop_titlebar_state();
  8339.         redraw_titlebar();
  8340.         if(ps_global->redrawer = redraw) /* reset old value, and test */
  8341.           (*ps_global->redrawer)();
  8342.     }
  8343.             
  8344.     }while(rc == 2 ||
  8345.        rc == 3 ||
  8346.        rc == 4 ||
  8347.        nickname_check(edit_buf, &error) ||
  8348.            ((check =
  8349.            adrbk_lookup_by_nick(abook, edit_buf, (adrbk_cntr_t *)NULL)) &&
  8350.          check != passed_in_ae));
  8351.  
  8352.     if(rc != 0){
  8353.     if(passed_in_ae)
  8354.       (void)adrbk_get_ae(abook, (a_c_arg_t)dl->elnum, Unlock);
  8355.  
  8356.     return -10;
  8357.     }
  8358.  
  8359.     /* only the case of nickname changed */
  8360.     if(passed_in_ae && check == passed_in_ae && strcmp(edit_buf, orig)){
  8361.     (void)adrbk_get_ae(abook, (a_c_arg_t)dl->elnum, Unlock);
  8362.     strcpy(orig, edit_buf);
  8363.     return -7;
  8364.     }
  8365.  
  8366.     if(passed_in_ae)
  8367.       (void)adrbk_get_ae(abook, (a_c_arg_t)dl->elnum, Unlock);
  8368.  
  8369.     if(strcmp(edit_buf, orig) == 0) /* no change */
  8370.       return -9;
  8371.     
  8372.     strcpy(orig, edit_buf);
  8373.     return 0;
  8374. }
  8375.  
  8376.  
  8377. /*
  8378.  * return values of search_in_one_line are or'd combination of these
  8379.  */
  8380. #define MATCH_NICK      0x1  /* match in field 0 */
  8381. #define MATCH_FULL      0x2  /* match in field 1 */
  8382. #define MATCH_ADDR      0x4  /* match in field 2 */
  8383. #define MATCH_FCC       0x8  /* match in fcc field */
  8384. #define MATCH_COMMENT  0x10  /* match in comment field */
  8385. #define MATCH_BIGFIELD 0x20  /* match in one of the fields that crosses the
  8386.                 whole screen, like a Title field */
  8387. #define MATCH_LISTMEM  0x40  /* match list member */
  8388. /*
  8389.  * Prompt user for search string and call find_in_book.
  8390.  *
  8391.  * Args: cur_line     -- The current line position (in global display list)
  8392.  *             of cursor
  8393.  *       command_line -- The screen line to prompt on
  8394.  *       new_line     -- Return value: new line position
  8395.  *       wrapped      -- Wrapped to beginning of display, tell user
  8396.  *       warped       -- Warped to a new location in the addrbook
  8397.  *
  8398.  * Result: The new line number is set if the search is successful.
  8399.  *         Returns 0 if found, -1 if not, -2 if cancelled.
  8400.  *       
  8401.  */
  8402. int
  8403. search_book(cur_line, command_line, new_line, wrapped, warped)
  8404.     long cur_line;
  8405.     int  command_line;
  8406.     long *new_line;
  8407.     int  *wrapped,
  8408.      *warped;
  8409. {
  8410.     int          find_result, rc;
  8411.     static char  search_string[MAX_SEARCH + 1] = { '\0' };
  8412.     char         prompt[MAX_SEARCH + 50], nsearch_string[MAX_SEARCH+1];
  8413.     HelpType     help;
  8414.     ESCKEY_S     ekey[4];
  8415.     PerAddrBook *pab;
  8416.     long         nl;
  8417.  
  8418.     dprint(7, (debugfile, "- search_book -\n"));
  8419.  
  8420.     sprintf(prompt, "Word to search for [%s]: ", search_string);
  8421.     help              = NO_HELP;
  8422.     nsearch_string[0] = '\0';
  8423.  
  8424.     ekey[0].ch    = 0;
  8425.     ekey[0].rval  = 0;
  8426.     ekey[0].name  = "";
  8427.     ekey[0].label = "";
  8428.  
  8429.     ekey[1].ch    = ctrl('Y');
  8430.     ekey[1].rval  = 10;
  8431.     ekey[1].name  = "^Y";
  8432.     ekey[1].label = "First Adr";
  8433.  
  8434.     ekey[2].ch    = ctrl('V');
  8435.     ekey[2].rval  = 11;
  8436.     ekey[2].name  = "^V";
  8437.     ekey[2].label = "Last Adr";
  8438.  
  8439.     ekey[3].ch    = -1;
  8440.  
  8441.     while(1){
  8442.         rc = optionally_enter(nsearch_string, command_line, 0, MAX_SEARCH,
  8443.                               1, 0, prompt, ekey, help, 0);
  8444.         if(rc == 3){
  8445.             help = help == NO_HELP ? h_oe_searchab : NO_HELP;
  8446.             continue;
  8447.         }
  8448.     else if(rc == 10){
  8449.         *warped = 1;
  8450.         warp_to_beginning();  /* go to top of addrbooks */
  8451.         if((nl=first_selectable_line(0L)) != NO_LINE){
  8452.         *new_line = nl;
  8453.         q_status_message(SM_INFO, 0, 2, "Searched to first entry");
  8454.         return 0;
  8455.         }
  8456.         else{
  8457.         q_status_message(SM_INFO, 0, 2, "No entries");
  8458.         return -1;
  8459.         }
  8460.     }
  8461.     else if(rc == 11){
  8462.         *warped = 1;
  8463.         warp_to_end();  /* go to bottom */
  8464.         if((nl=first_selectable_line(0L)) != NO_LINE){
  8465.         *new_line = nl;
  8466.         q_status_message(SM_INFO, 0, 2, "Searched to last entry");
  8467.         return 0;
  8468.         }
  8469.         else{
  8470.         q_status_message(SM_INFO, 0, 2, "No entries");
  8471.         return -1;
  8472.         }
  8473.     }
  8474.  
  8475.         if(rc != 4)
  8476.           break;
  8477.     }
  8478.  
  8479.         
  8480.     if(rc == 1 || (search_string[0] == '\0' && nsearch_string[0] == '\0'))
  8481.       return -2;
  8482.  
  8483.     if(nsearch_string[0] != '\0'){
  8484.         search_string[MAX_SEARCH] = '\0';
  8485.         strncpy(search_string, nsearch_string, MAX_SEARCH);
  8486.     }
  8487.  
  8488.     find_result = find_in_book(cur_line, search_string, new_line, wrapped);
  8489.     
  8490.     if(*wrapped)
  8491.       *warped = 1;
  8492.  
  8493.     if(find_result){
  8494.     int also = 0, notdisplayed = 0;
  8495.  
  8496.     pab = &as.adrbks[adrbk_num_from_lineno(*new_line)];
  8497.     if(find_result & MATCH_NICK){
  8498.         if(pab->nick_is_displayed)
  8499.           also++;
  8500.         else
  8501.           notdisplayed++;
  8502.     }
  8503.  
  8504.     if(find_result & MATCH_FULL){
  8505.         if(pab->full_is_displayed)
  8506.           also++;
  8507.         else
  8508.           notdisplayed++;
  8509.     }
  8510.  
  8511.     if(find_result & MATCH_ADDR){
  8512.         if(pab->addr_is_displayed)
  8513.           also++;
  8514.         else
  8515.           notdisplayed++;
  8516.     }
  8517.  
  8518.     if(find_result & MATCH_FCC){
  8519.         if(pab->fcc_is_displayed)
  8520.           also++;
  8521.         else
  8522.           notdisplayed++;
  8523.     }
  8524.  
  8525.     if(find_result & MATCH_COMMENT){
  8526.         if(pab->comment_is_displayed)
  8527.           also++;
  8528.         else
  8529.           notdisplayed++;
  8530.     }
  8531.  
  8532.     if(find_result & MATCH_LISTMEM){
  8533.         AddrScrn_Disp *dl;
  8534.  
  8535.         dl = dlist(*new_line);
  8536.         if(F_OFF(F_EXPANDED_DISTLISTS,ps_global)
  8537.           && !exp_is_expanded(pab->address_book->exp, (a_c_arg_t)dl->elnum))
  8538.           notdisplayed++;
  8539.     }
  8540.  
  8541.     if(notdisplayed > 1)
  8542.       q_status_message3(SM_ORDER,0,4, "%satched string in %s %sfields",
  8543.           also ? "Also m" : "M",
  8544.           comatose(notdisplayed),
  8545.           also ? "other " : "");
  8546.     else if(notdisplayed == 1)
  8547.       q_status_message2(SM_ORDER,0,4, "%satched string in %s",
  8548.           also ? "Also m" : "M",
  8549.           (find_result & MATCH_NICK && !pab->nick_is_displayed)     ?
  8550.                                 "Nickname field"
  8551.                 : (find_result & MATCH_FULL && !pab->full_is_displayed) ?
  8552.                                 "Fullname field"
  8553.                 : (find_result & MATCH_ADDR && !pab->addr_is_displayed) ?
  8554.                                 "Address field"
  8555.                 : (find_result & MATCH_FCC && !pab->fcc_is_displayed)   ?
  8556.                                 "Fcc field"
  8557.                 : (find_result & MATCH_COMMENT && !pab->comment_is_displayed) ?
  8558.                                 "Comment field"
  8559.                 : (find_result & MATCH_LISTMEM) ? "list member address" : "?");
  8560.  
  8561.  
  8562.     /* be sure to be on a selectable field */
  8563.     if(!line_is_selectable(*new_line))
  8564.       if((nl=first_selectable_line(*new_line+1)) != NO_LINE)
  8565.         *new_line = nl;
  8566.     }
  8567.  
  8568.     return(find_result ? 0 : -1);
  8569. }
  8570.  
  8571.  
  8572. /*
  8573.  * Search the display list for the given string.
  8574.  *
  8575.  * Args: cur_line     -- The current line position (in global display list)
  8576.  *             of cursor
  8577.  *       string       -- String to search for
  8578.  *       new_line     -- Return value: new line position
  8579.  *       wrapped      -- Wrapped to beginning of display during search
  8580.  *
  8581.  * Result: The new line number is set if the search is successful.
  8582.  *         Returns 0 -- string not found
  8583.  *          Otherwise, a bitmask of which fields the string was found in.
  8584.  */
  8585. int
  8586. find_in_book(cur_line, string, new_line, wrapped)
  8587.     long  cur_line;
  8588.     char *string;
  8589.     long *new_line;
  8590.     int  *wrapped;
  8591. {
  8592.     register AddrScrn_Disp *dl;
  8593.     long                    nl, nl_save;
  8594.     int                fields;
  8595.     AdrBk_Entry            *abe;
  8596.     char                   *listaddr = NULL;
  8597.     DL_CACHE_S             *dlc,
  8598.                 dlc_save; /* a local copy */
  8599.  
  8600.  
  8601.     dprint(9, (debugfile, "- find_in_book -\n"));
  8602.  
  8603.     /*
  8604.      * Save info to allow us to get back to where we were if we can't find
  8605.      * the string.  Also used to stop our search if we wrap back to the
  8606.      * start and search forward.
  8607.      */
  8608.  
  8609.     nl_save = cur_line;
  8610.     dlc = get_dlc(nl_save);
  8611.     dlc_save = *dlc;
  8612.  
  8613.     *wrapped = 0;
  8614.     nl = cur_line + 1L;
  8615.  
  8616.     /* start with next line and search to the end of the disp_list */
  8617.     dl = dlist(nl);
  8618.     while(dl->type != End){
  8619.     if(dl->type == Simple ||
  8620.        dl->type == ListHead ||
  8621.        dl->type == ListEnt ||
  8622.        dl->type == ListClickHere){
  8623.         abe = ae(nl);
  8624.         if(dl->type == ListEnt)
  8625.           listaddr = listmem(nl);
  8626.     }
  8627.     else
  8628.       abe = (AdrBk_Entry *)NULL;
  8629.  
  8630.     if(fields=search_in_one_line(dl, abe, listaddr, string))
  8631.       goto found;
  8632.  
  8633.     dl = dlist(++nl);
  8634.     }
  8635.  
  8636.  
  8637.     /*
  8638.      * Wrap back to the start of the addressbook and search forward
  8639.      * from there.
  8640.      */
  8641.     warp_to_beginning();  /* go to top of addrbooks */
  8642.     nl = 0L;  /* line number is always 0 after warp_to_beginning */
  8643.     *wrapped = 1;
  8644.  
  8645.     dlc = get_dlc(nl);
  8646.     while(!matching_dlcs(&dlc_save, dlc) && dlc->type != DlcEnd){
  8647.  
  8648.     fill_in_dl_field(dlc);
  8649.     dl = &dlc->dl;
  8650.  
  8651.     if(dl->type == Simple ||
  8652.        dl->type == ListHead ||
  8653.        dl->type == ListEnt ||
  8654.        dl->type == ListClickHere){
  8655.         abe = ae(nl);
  8656.         if(dl->type == ListEnt)
  8657.           listaddr = listmem(nl);
  8658.     }
  8659.     else
  8660.       abe = (AdrBk_Entry *)NULL;
  8661.  
  8662.     if(fields=search_in_one_line(dl, abe, listaddr, string))
  8663.       goto found;
  8664.  
  8665.     dlc = get_dlc(++nl);
  8666.     }
  8667.  
  8668.     /* see if it is in the current line */
  8669.     fill_in_dl_field(dlc);
  8670.     dl = &dlc->dl;
  8671.  
  8672.     if(dl->type == Simple ||
  8673.        dl->type == ListHead ||
  8674.        dl->type == ListEnt ||
  8675.        dl->type == ListClickHere){
  8676.     abe = ae(nl);
  8677.     if(dl->type == ListEnt)
  8678.       listaddr = listmem(nl);
  8679.     }
  8680.     else
  8681.       abe = (AdrBk_Entry *)NULL;
  8682.  
  8683.     fields = search_in_one_line(dl, abe, listaddr, string);
  8684.     if((dl->type == Text || dl->type == Title) && dl->txt)
  8685.       fs_give((void **)&dl->txt);
  8686.  
  8687.     /* jump cache back to where we started */
  8688.     *wrapped = 0;
  8689.     warp_to_dlc(&dlc_save, nl_save);
  8690.     if(fields)
  8691.       *new_line = nl_save;  /* because it was in current line */
  8692.  
  8693.     nl = *new_line;
  8694.  
  8695. found:
  8696.     *new_line = nl;
  8697.     return(fields);
  8698. }
  8699.  
  8700.  
  8701. /*
  8702.  * Look in line dl for string.
  8703.  *
  8704.  * Args: dl     -- the display list for this line
  8705.  *       abe    -- AdrBk_Entry if it is an address type
  8706.  *     listaddr -- list member if it is of type ListEnt
  8707.  *       string -- look for this string
  8708.  *
  8709.  * Result:  0   -- string not found
  8710.  *          Otherwise, a bitmask of which fields the string was found in.
  8711.  *      MATCH_NICK      0x1
  8712.  *      MATCH_FULL      0x2
  8713.  *      MATCH_ADDR      0x4
  8714.  *      MATCH_FCC       0x8
  8715.  *      MATCH_COMMENT  0x10
  8716.  *      MATCH_BIGFIELD 0x20
  8717.  *      MATCH_LISTMEM  0x40
  8718.  */
  8719. int
  8720. search_in_one_line(dl, abe, listaddr, string)
  8721.     AddrScrn_Disp *dl;
  8722.     AdrBk_Entry   *abe;
  8723.     char          *listaddr;
  8724.     char          *string;
  8725. {
  8726.     register int c;
  8727.     int ret_val = 0;
  8728.     char **lm;
  8729.  
  8730.     for(c = 0; c < 5; c++){
  8731.       switch(c){
  8732.     case 0:
  8733.       switch(dl->type){
  8734.         case Simple:
  8735.         case ListHead:
  8736.           if(srchstr(abe->nickname, string))
  8737.         ret_val |= MATCH_NICK;
  8738.  
  8739.           break;
  8740.  
  8741.         case Text:
  8742.         case Title:
  8743.           if(srchstr(dl->txt, string))
  8744.         ret_val |= MATCH_BIGFIELD;
  8745.       }
  8746.       break;
  8747.  
  8748.     case 1:
  8749.       switch(dl->type){
  8750.         case Simple:
  8751.         case ListHead:
  8752.           if(abe && srchstr(
  8753.             (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  8754.                         abe->fullname, NULL),
  8755.             string))
  8756.         ret_val |= MATCH_FULL;
  8757.       }
  8758.       break;
  8759.  
  8760.     case 2:
  8761.       switch(dl->type){
  8762.         case Simple:
  8763.           if(srchstr((abe && abe->tag == Single) ?
  8764.           abe->addr.addr : NULL, string))
  8765.         ret_val |= MATCH_ADDR;
  8766.  
  8767.           break;
  8768.  
  8769.         case ListEnt:
  8770.           if(srchstr(
  8771.          (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  8772.                              listaddr, NULL), string))
  8773.         ret_val |= MATCH_LISTMEM;
  8774.  
  8775.           break;
  8776.  
  8777.         case ClickHere:
  8778.           if(srchstr(CLICKHERE, string))
  8779.         ret_val |= MATCH_BIGFIELD;
  8780.  
  8781.           break;
  8782.  
  8783.         case ListClickHere:
  8784.           if(abe)
  8785.             for(lm = abe->addr.list;
  8786.             !(ret_val & MATCH_LISTMEM) && *lm; lm++)
  8787.               if(srchstr(*lm, string))
  8788.             ret_val |= MATCH_LISTMEM;
  8789.  
  8790.           break;
  8791.  
  8792.         case Empty:
  8793.         case ListEmpty:
  8794.           if(srchstr(EMPTY, string))
  8795.         ret_val |= MATCH_BIGFIELD;
  8796.  
  8797.           break;
  8798.       }
  8799.       break;
  8800.  
  8801.     case 3:  /* fcc */
  8802.       switch(dl->type){
  8803.         case Simple:
  8804.         case ListHead:
  8805.           if(abe && srchstr(abe->fcc, string))
  8806.         ret_val |= MATCH_FCC;
  8807.       }
  8808.       break;
  8809.  
  8810.     case 4:  /* comment */
  8811.       switch(dl->type){
  8812.         case Simple:
  8813.         case ListHead:
  8814.           if(abe && srchstr(
  8815.                 (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  8816.                         abe->extra, NULL),
  8817.             string))
  8818.         ret_val |= MATCH_COMMENT;
  8819.       }
  8820.       break;
  8821.       }
  8822.     }
  8823.  
  8824.     return(ret_val);
  8825. }
  8826.  
  8827.  
  8828. /*
  8829.  * Add an entry to address book.
  8830.  * It is for capturing addresses off incoming mail.
  8831.  * This is a front end for take_to_addrbooks.
  8832.  * It is also used for replacing an existing entry and for adding a single
  8833.  * new address to an existing list.
  8834.  *
  8835.  * The reason this is here is so that when Taking a single address, we can
  8836.  * rearrange the fullname to be Last, First instead of First Last.
  8837.  *
  8838.  * Args: ta_entry -- the entry from the take screen
  8839.  *   command_line -- line to prompt on
  8840.  *
  8841.  * Result: item is added to one of the address books,
  8842.  *       an error message is queued if appropriate.
  8843.  */
  8844. void
  8845. add_abook_entry(ta_entry, nick, fullname, fcc, comment, command_line)
  8846.     TA_S *ta_entry;
  8847.     char *nick;
  8848.     char *fullname;
  8849.     char *fcc;
  8850.     char *comment;
  8851.     int   command_line;
  8852. {
  8853.     ADDRESS *addr;
  8854.     char     new_fullname[MAX_FULLNAME + 1],
  8855.          new_address[MAX_ADDRESS + 1];
  8856.     char   **new_list, *charset = NULL;
  8857.     char    *old_fullname;
  8858.     int      need_to_encode = 0;
  8859.  
  8860.     dprint(2, (debugfile, "-- add_abook_entry --\n"));
  8861.  
  8862.     /*-- rearrange full name (Last, First) ---*/
  8863.     new_fullname[0]              = '\0';
  8864.     new_fullname[MAX_FULLNAME]   = '\0';
  8865.     new_fullname[MAX_FULLNAME-1] = '\0';
  8866.     addr = ta_entry->addr;
  8867.     if(!fullname && addr->personal != NULL){
  8868.     /*
  8869.      * In order to swap First Last to Last, First we have to decode
  8870.      * and re-encode.
  8871.      */
  8872.     old_fullname = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  8873.                         addr->personal, &charset);
  8874.     if(old_fullname == addr->personal){
  8875.         if(charset)
  8876.           fs_give((void **)&charset);
  8877.  
  8878.         charset = NULL;
  8879.     }
  8880.     else
  8881.       need_to_encode++;
  8882.  
  8883.     if(strindex(old_fullname, ',') != NULL){
  8884.         int add_quotes = 0;
  8885.         char *nf;
  8886.  
  8887.         nf = new_fullname;
  8888.         /*
  8889.          * We'll get this wrong if it is already quoted but the quote
  8890.          * doesn't start right at the beginning.
  8891.          */
  8892.         if(old_fullname[0] != '"'){
  8893.         add_quotes++;
  8894.         *nf++ = '"';
  8895.         }
  8896.  
  8897.         strncpy(nf, old_fullname, MAX_FULLNAME-2);
  8898.         if(add_quotes)
  8899.           strcat(nf, "\"");
  8900.     }
  8901.     else if(strindex(old_fullname, SPACE) == NULL){  /* leave word */
  8902.         strncpy(new_fullname, old_fullname, MAX_FULLNAME);
  8903.     }
  8904.     else{
  8905.         char *p, *q, *r;
  8906.  
  8907.         /* switch to Last, First */
  8908.         p = old_fullname;
  8909.         while((q = strindex(p, SPACE)) != NULL)
  8910.           p = q + 1;
  8911.  
  8912.         for(q = p, r = new_fullname; *q; *r++ = *q++)
  8913.           ;/* do nothing */
  8914.  
  8915.         *r++ = ',';
  8916.         *r++ = SPACE;
  8917.         for(q = old_fullname; q < p; *r++ = *q++)
  8918.           ;/* do nothing */
  8919.  
  8920.         *r = '\0';
  8921.         for(r--;
  8922.         r >= new_fullname && isspace((unsigned char)*r);
  8923.         *r-- = '\0')
  8924.           ;/* do nothing */
  8925.     }
  8926.     }
  8927.  
  8928.     if(need_to_encode){
  8929.     char *s;
  8930.     char  buf[MAX_FULLNAME + 10];
  8931.  
  8932.     /* re-encode in original charset */
  8933.     s = rfc1522_encode(buf, (unsigned char *)new_fullname,
  8934.         charset ? charset : ps_global->VAR_CHAR_SET);
  8935.     if(s != new_fullname)
  8936.       strncpy(new_fullname, s, MAX_FULLNAME);
  8937.     
  8938.     new_fullname[MAX_FULLNAME-1] = '\0';
  8939.     if(charset)
  8940.       fs_give((void **)&charset);
  8941.     }
  8942.  
  8943.     /* initial value for new address */
  8944.     new_address[0] = '\0';
  8945.     new_address[MAX_ADDRESS] = '\0';
  8946.     if(addr->mailbox){
  8947.     char *scratch, *p, *t, *u;
  8948.     unsigned long l;
  8949.  
  8950.     scratch = (char *)fs_get((size_t)est_size(addr));
  8951.     scratch[0] = '\0';
  8952.     rfc822_write_address(scratch, addr);
  8953.     if(p = srchstr(scratch, "@.RAW-FIELD.")){
  8954.       for(t = p; ; t--)
  8955.         if(*t == '&'){        /* find "leading" token */
  8956.         *t++ = ' ';        /* replace token */
  8957.         *p = '\0';        /* tie off string */
  8958.         u = (char *)rfc822_base64((unsigned char *)t,
  8959.                       (unsigned long)strlen(t), &l);
  8960.         *p = '@';        /* restore 'p' */
  8961.         rplstr(p, 12, "");    /* clear special token */
  8962.         rplstr(t, strlen(t), u);
  8963.         fs_give((void **)&u);
  8964.         }
  8965.         else if(t == scratch)
  8966.           break;
  8967.     }
  8968.  
  8969.     strncpy(new_address, scratch, MAX_ADDRESS);
  8970.  
  8971.     if(scratch)
  8972.       fs_give((void **)&scratch);
  8973.     }
  8974.  
  8975.     if(ta_entry->frwrded){
  8976.     ADDRESS *a;
  8977.     int i;
  8978.     char buf[MAX_ADDR_EXPN+1];
  8979.  
  8980.     for(i = 0, a = addr; a; i++, a = a->next)
  8981.       ;/* just counting for alloc below */
  8982.  
  8983.     new_list = (char **)fs_get((i+1) * sizeof(char *));
  8984.     for(i = 0, a = addr; a; i++, a = a->next)
  8985.       new_list[i] = cpystr(addr_string(a, buf));
  8986.  
  8987.     new_list[i] = NULL;
  8988.     }
  8989.     else{
  8990.     new_list = (char **)fs_get(2 * sizeof(char *));
  8991.     new_list[0] = cpystr(ta_entry->strvalue);
  8992.     new_list[1] = NULL;
  8993.     }
  8994.  
  8995.     take_to_addrbooks_frontend(new_list, nick,
  8996.                    fullname ? fullname : new_fullname,
  8997.                    new_address, fcc, comment, command_line);
  8998.     free_list(&new_list);
  8999. }
  9000.  
  9001.  
  9002. void
  9003. take_to_addrbooks_frontend(new_entries, nick, fullname, addr, fcc,
  9004.                 comment, cmdline)
  9005.     char **new_entries;
  9006.     char  *nick;
  9007.     char  *fullname;
  9008.     char  *addr;
  9009.     char  *fcc;
  9010.     char  *comment;
  9011.     int    cmdline;
  9012. {
  9013.     jmp_buf        save_jmp_buf;
  9014.  
  9015.     dprint(2, (debugfile, "-- take_to_addrbooks_frontend --\n"));
  9016.  
  9017.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  9018.     if(setjmp(addrbook_changed_unexpectedly)){
  9019.     q_status_message(SM_ORDER, 5, 10, "Resetting address book...");
  9020.     dprint(1, (debugfile,
  9021.         "RESETTING address book... take_to_addrbooks_frontend!\n"));
  9022.     addrbook_reset();
  9023.     }
  9024.  
  9025.     take_to_addrbooks(new_entries, nick, fullname, addr, fcc, comment, cmdline);
  9026.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  9027. }
  9028.  
  9029.  
  9030. /*
  9031.  * Add to address book, called from take screen.
  9032.  * It is also used for adding to an existing list or replacing an existing
  9033.  * entry.
  9034.  *
  9035.  * Args: new_entries -- a list of addresses to add to a list or to form
  9036.  *                      a new list with
  9037.  *              nick -- if adding new entry, suggest this for nickname
  9038.  *          fullname -- if adding new entry, use this for fullname
  9039.  *              addr -- if only one new_entry, this is its addr
  9040.  *               fcc -- if adding new entry, use this for fcc
  9041.  *           comment -- if adding new entry, use this for comment
  9042.  *      command_line -- line to prompt on
  9043.  *
  9044.  * Result: item is added to one of the address books,
  9045.  *       an error message is queued if appropriate.
  9046.  */
  9047. void
  9048. take_to_addrbooks(new_entries, nick, fullname, addr, fcc, comment, command_line)
  9049.     char **new_entries;
  9050.     char  *nick;
  9051.     char  *fullname;
  9052.     char  *addr;
  9053.     char  *fcc;
  9054.     char  *comment;
  9055.     int    command_line;
  9056. {
  9057.     char          new_nickname[MAX_NICKNAME + 1];
  9058.     char          prompt[200], **p;
  9059.     int           rc, listadd = 0, ans, i;
  9060.     AdrBk        *abook;
  9061.     SAVE_STATE_S  state;
  9062.     PerAddrBook  *pab;
  9063.     AdrBk_Entry  *abe = (AdrBk_Entry *)NULL, *abe_copy;
  9064.     adrbk_cntr_t  entry_num = NO_NEXT;
  9065.     size_t        tot_size, new_size, old_size;
  9066.     Tag           old_tag = NotSet;
  9067.  
  9068.     dprint(2, (debugfile, "-- take_to_addrbooks --\n"));
  9069.  
  9070.     pab = setup_for_addrbook_add(&state, command_line);
  9071.  
  9072.     /* check we got it opened ok */
  9073.     if(pab == NULL || pab->address_book == NULL)
  9074.       goto take_to_addrbooks_cancel;
  9075.  
  9076.     abook = pab->address_book;
  9077.  
  9078.     /*----- nickname ------*/
  9079.     sprintf(prompt,
  9080.       "Enter new or existing nickname (one word and easy to remember): ");
  9081.     new_nickname[0] = '\0';
  9082.     if(nick){
  9083.     strncpy(new_nickname, nick, MAX_NICKNAME);
  9084.     new_nickname[MAX_NICKNAME] = '\0';
  9085.     }
  9086.  
  9087.     rc = edit_nickname(abook, (AddrScrn_Disp *)NULL, command_line,
  9088.         new_nickname, prompt, h_oe_takenick, 1, 1);
  9089.     if(rc == -8){  /* this means an existing nickname was entered */
  9090.     static ESCKEY_S choices[] = {
  9091.         {'r', 'r', "R", "Replace"},
  9092.         {'a', 'a', "A", "Add"},
  9093.         {-1, 0, NULL, NULL}};
  9094.  
  9095.     abe = adrbk_lookup_by_nick(abook, new_nickname, &entry_num);
  9096.     if(!abe){  /* this shouldn't happen */
  9097.         q_status_message1(SM_ORDER, 0, 4,
  9098.         "Already an entry %s in address book!",
  9099.         new_nickname);
  9100.         goto take_to_addrbooks_cancel;
  9101.     }
  9102.  
  9103.     old_tag = abe->tag;
  9104.  
  9105.     sprintf(prompt,
  9106.         "%s %s (%s) exists, replace or add addresses to it ? ",
  9107.         abe->tag == List ? "List" : "Entry",
  9108.         new_nickname,
  9109.         (abe->fullname && abe->fullname[0])
  9110.             ? (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  9111.                             abe->fullname, NULL)
  9112.             : "<no long name>");
  9113.  
  9114.     ans = radio_buttons(prompt,
  9115.                 command_line,
  9116.                 choices,
  9117.                 'a',
  9118.                 'x',
  9119.                 h_oe_take_or_replace,
  9120.                 RB_NORM);
  9121.     if(ans == 'a')
  9122.       listadd++;
  9123.     else if(ans != 'r')
  9124.       goto take_to_addrbooks_cancel;
  9125.     }
  9126.     else if(rc != 0 && rc != -9)  /* -9 means a null nickname */
  9127.       goto take_to_addrbooks_cancel;
  9128.  
  9129.     if((long)abook->count > MAX_ADRBK_SIZE ||
  9130.        (old_tag == NotSet && (long)abook->count >= MAX_ADRBK_SIZE)){
  9131.     q_status_message(SM_ORDER, 3, 5,
  9132.         "Address book is at maximum size. TakeAddr cancelled.");
  9133.     dprint(2, (debugfile, "Addrbook at Max size, TakeAddr cancelled\n"));
  9134.     goto take_to_addrbooks_cancel;
  9135.     }
  9136.  
  9137.     if(listadd){
  9138.     /* count up size of existing list */
  9139.     if(abe->tag == List){
  9140.         for(p = abe->addr.list; p != NULL && *p != NULL; p++)
  9141.           ;/* do nothing */
  9142.     
  9143.         old_size = p - abe->addr.list;
  9144.     }
  9145.     /* or size of existing single address */
  9146.     else
  9147.       old_size = 1;
  9148.     }
  9149.     else /* don't care about old size, they will be tossed in edit_entry */
  9150.       old_size = 0;
  9151.  
  9152.     /* make up an abe to pass to edit_entry */
  9153.     abe_copy = adrbk_newentry();
  9154.     abe_copy->nickname = cpystr(new_nickname);
  9155.     abe_copy->tag = List;
  9156.  
  9157.     if(listadd){
  9158.     abe_copy->fullname = cpystr((abe->fullname && abe->fullname[0])
  9159.                             ? abe->fullname : "");
  9160.     abe_copy->fcc   = cpystr((abe->fcc && abe->fcc[0]) ? abe->fcc : "");
  9161.     abe_copy->extra = cpystr((abe->extra&&abe->extra[0]) ? abe->extra : "");
  9162.     }
  9163.     else{
  9164.     /* use passed in info if available */
  9165.     abe_copy->fullname = cpystr(fullname ? fullname : "");
  9166.     abe_copy->fcc      = cpystr(fcc ? fcc : "");
  9167.     abe_copy->extra    = cpystr(comment ? comment : "");
  9168.     }
  9169.  
  9170.     /* count up size of new list */
  9171.     for(p = new_entries; p != NULL && *p != NULL; p++)
  9172.       ;/* do nothing */
  9173.     
  9174.     new_size = p - new_entries;
  9175.     tot_size = old_size + new_size;
  9176.     abe_copy->addr.list = (char **)fs_get((tot_size+1) * sizeof(char *));
  9177.     memset((void *)abe_copy->addr.list, 0, (tot_size+1) * sizeof(char *));
  9178.     if(old_size > 0){
  9179.     if(abe->tag == List){
  9180.         for(i = 0; i < old_size; i++)
  9181.           abe_copy->addr.list[i] = cpystr(abe->addr.list[i]);
  9182.     }
  9183.     else
  9184.       abe_copy->addr.list[0] = cpystr(abe->addr.addr);
  9185.     }
  9186.  
  9187.     /* add new addresses to list */
  9188.     if(tot_size == 1 && addr)
  9189.       abe_copy->addr.list[0] = cpystr(addr);
  9190.     else
  9191.       for(i = 0; i < new_size; i++)
  9192.         abe_copy->addr.list[old_size + i] = cpystr(new_entries[i]);
  9193.  
  9194.     abe_copy->addr.list[tot_size] = NULL;
  9195.  
  9196.     edit_entry(abook, abe_copy, (a_c_arg_t)entry_num, old_tag, 0, NULL);
  9197.  
  9198.     /* free copy */
  9199.     free_ae(abook, &abe_copy);
  9200.     restore_state(&state);
  9201.     return;
  9202.  
  9203. take_to_addrbooks_cancel:
  9204.     cancel_warning(NO_DING, "addition");
  9205.     restore_state(&state);
  9206. }
  9207.  
  9208.  
  9209. /*
  9210.  * Prep addrbook for TakeAddr add operation.
  9211.  *
  9212.  * Arg: savep -- Address of a pointer to save addrbook state in.
  9213.  *      stp   -- Address of a pointer to save addrbook state in.
  9214.  *
  9215.  * Returns: a PerAddrBook pointer, or NULL.
  9216.  */
  9217. PerAddrBook *
  9218. setup_for_addrbook_add(state, command_line)
  9219.     SAVE_STATE_S *state;
  9220.     int              command_line;
  9221. {
  9222.     PerAddrBook *pab;
  9223.  
  9224.     init_ab_if_needed();
  9225.     save_state(state);
  9226.  
  9227.     if(as.n_addrbk == 0){
  9228.         q_status_message(SM_ORDER, 3, 4, "Can't open address book!");
  9229.         return NULL;
  9230.     }
  9231.     else
  9232.       pab = use_this_addrbook(command_line);
  9233.     
  9234.     if(!pab)
  9235.       return NULL;
  9236.  
  9237.     /* initialize addrbook so we can add to it */
  9238.     init_abook(pab, Open);
  9239.  
  9240.     if(pab->ostatus != Open){
  9241.         q_status_message(SM_ORDER, 3, 4, "Can't open address book!");
  9242.         return NULL;
  9243.     }
  9244.  
  9245.     if(pab->access != ReadWrite){
  9246.     if(pab->access == ReadOnly)
  9247.       readonly_warning(NO_DING, NULL);
  9248.     else if(pab->access == NoAccess)
  9249.       q_status_message(SM_ORDER, 3, 4,
  9250.         "AddressBook not accessible, permission denied");
  9251.  
  9252.         return NULL;
  9253.     }
  9254.  
  9255.     return(pab);
  9256. }
  9257.  
  9258.  
  9259. /*
  9260.  *  Interface to address book lookups for callers outside or inside this file.
  9261.  *
  9262.  * Args: nickname       -- The nickname to look up
  9263.  *       which_addrbook -- If matched, addrbook number it was found in.
  9264.  *       not_here       -- If non-negative, skip looking in this abook.
  9265.  *
  9266.  * Result: returns NULL or the corresponding fullname.  The fullname is
  9267.  * allocated here so the caller must free it.
  9268.  *
  9269.  * This opens the address books if they haven't been opened and restores
  9270.  * them to the state they were in upon entry.
  9271.  */
  9272. char *
  9273. addr_lookup(nickname, which_addrbook, not_here)
  9274.     char *nickname;
  9275.     int  *which_addrbook;
  9276.     int   not_here;
  9277. {
  9278.     AdrBk_Entry  *abe;
  9279.     SAVE_STATE_S  state;
  9280.     char         *fullname;
  9281.  
  9282.     dprint(9, (debugfile, "- addr_lookup -\n"));
  9283.  
  9284.     init_ab_if_needed();
  9285.     save_state(&state);
  9286.  
  9287.     abe = adrbk_lookup_with_opens_by_nick(nickname,0,which_addrbook,not_here);
  9288.  
  9289.     fullname = (abe && abe->fullname) ? cpystr(abe->fullname) : NULL;
  9290.  
  9291.     restore_state(&state);
  9292.  
  9293.     return(fullname);
  9294. }
  9295.  
  9296.  
  9297. /*
  9298.  * These chars in nicknames will mess up parsing.
  9299.  *
  9300.  * Returns 0 if ok, 1 if not.
  9301.  * Returns an allocated error message on error.
  9302.  */
  9303. int
  9304. nickname_check(nickname, error)
  9305.     char  *nickname;
  9306.     char **error;
  9307. {
  9308.     register char *t;
  9309.     char buf[100];
  9310.  
  9311.     if((t = strindex(nickname, SPACE)) ||
  9312.        (t = strindex(nickname, ',')) ||
  9313.        (t = strindex(nickname, '"')) ||
  9314.        (t = strindex(nickname, ';')) ||
  9315.        (t = strindex(nickname, ':')) ||
  9316.        (t = strindex(nickname, '@')) ||
  9317.        (t = strindex(nickname, '(')) ||
  9318.        (t = strindex(nickname, ')')) ||
  9319.        (t = strindex(nickname, '\\')) ||
  9320.        (t = strindex(nickname, '[')) ||
  9321.        (t = strindex(nickname, ']')) ||
  9322.        (t = strindex(nickname, '<')) ||
  9323.        (t = strindex(nickname, '>'))){
  9324.     char s[4];
  9325.     s[0] = '"';
  9326.     s[1] = *t;
  9327.     s[2] = '"';
  9328.     s[3] = '\0';
  9329.     if(error){
  9330.         sprintf(buf, "%s not allowed in nicknames",
  9331.         *t == SPACE ?
  9332.             "Blank spaces" :
  9333.             *t == ',' ?
  9334.             "Commas" :
  9335.             *t == '"' ?
  9336.                 "Quotes" :
  9337.                 s);
  9338.         *error = cpystr(buf);
  9339.     }
  9340.  
  9341.     return 1;
  9342.     }
  9343.  
  9344.     return 0;
  9345. }
  9346.  
  9347.  
  9348. /*
  9349.  * This is like build_address() only it doesn't close
  9350.  * everything down when it is done, and it doesn't open addrbooks that
  9351.  * are already open.  Other than that, it has the same functionality.
  9352.  * It opens addrbooks that haven't been opened and saves and restores the
  9353.  * addrbooks open states (if save_and_restore is set).
  9354.  *
  9355.  * Args: to                    -- the address to attempt expanding (see the
  9356.  *                   description in expand_address)
  9357.  *       full_to               -- a pointer to result
  9358.  *            This will be allocated here and freed by the caller.
  9359.  *       error                 -- a pointer to an error message, if non-null
  9360.  *       fcc                   -- a pointer to returned fcc, if non-null
  9361.  *            This will be allocated here and freed by the caller.
  9362.  *            *fcc should be null on entry.
  9363.  *       save_and_restore      -- restore addrbook states when finished
  9364.  *
  9365.  * Results:    0 -- address is ok
  9366.  *            -1 -- address is not ok
  9367.  * full_to contains the expanded address on success, or a copy of to
  9368.  *         on failure
  9369.  * *error  will point to an error message on failure it it is non-null
  9370.  *
  9371.  * Side effect: Can flush addrbook entry cache entries so they need to be
  9372.  * re-fetched afterwords.
  9373.  */
  9374. int
  9375. our_build_address(to, full_to, error, fcc, save_and_restore, simple_verify)
  9376.     BuildTo to;
  9377.     char  **full_to,
  9378.       **error,
  9379.       **fcc;
  9380.     int     save_and_restore, simple_verify;
  9381. {
  9382.     int ret;
  9383.  
  9384.     dprint(7, (debugfile, "- our_build_address -  (%s)\n",
  9385.     (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
  9386.              : (to.arg.abe->nickname ? to.arg.abe->nickname
  9387.                         : "no nick")));
  9388.  
  9389.     if(to.type == Str && !to.arg.str || to.type == Abe && !to.arg.abe){
  9390.     if(full_to)
  9391.       *full_to = cpystr("");
  9392.     ret = 0;
  9393.     }
  9394.     else
  9395.       ret = build_address_internal(to, full_to, error, fcc, NULL, NULL,
  9396.                    save_and_restore, simple_verify);
  9397.  
  9398.     dprint(8, (debugfile, "   our_build_address says %s address\n",
  9399.     ret ? "BAD" : "GOOD"));
  9400.  
  9401.     return(ret);
  9402. }
  9403.  
  9404.  
  9405. /*
  9406.  * This is the builder used by the composer for the Lcc line.
  9407.  *
  9408.  * Args: lcc     -- the passed in Lcc line to parse
  9409.  *      full_lcc -- Address of a pointer to return the full address in.
  9410.  *            This will be allocated here and freed by the caller.
  9411.  *       error   -- Address of a pointer to return an error message in.
  9412.  *                  This is not allocated so should not be freed by the caller.
  9413.  *       to_line -- This is a pointer to text for affected entries which
  9414.  *            we may be changing.  The first one in the list is the
  9415.  *            To entry.  We may put the name of the list in empty
  9416.  *            group syntax form there (like  List Name: ;).
  9417.  *            The second one in the list is the fcc field.
  9418.  *            The tptr members already point to text allocated in the
  9419.  *            caller.  We may free and reallocate here, caller will
  9420.  *            free the result in any case.
  9421.  *
  9422.  * Result:  0 is returned if address was OK, 
  9423.  *         -1 if address wasn't OK.
  9424.  *
  9425.  * Side effect: Can flush addrbook entry cache entries so they need to be
  9426.  * re-fetched afterwords.
  9427.  */
  9428. int
  9429. build_addr_lcc(lcc, full_lcc, error, to_line)
  9430.     char     *lcc,
  9431.         **full_lcc,
  9432.         **error;
  9433.     BUILDER_ARG     *to_line;
  9434. {
  9435.     int            ret_val,
  9436.             no_repo = 0;    /* fcc or lcc not reproducible */
  9437.     BuildTo        bldlcc;
  9438.     BuilderPrivate *bp = NULL;
  9439.     char       *p,
  9440.            *fcc_local = NULL,
  9441.            *to = NULL,
  9442.            *dummy;
  9443.     long        csum;
  9444.     jmp_buf         save_jmp_buf;
  9445.  
  9446.     dprint(2, (debugfile, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
  9447.  
  9448.     /* check to see if to string is empty to avoid work */
  9449.     for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
  9450.       ;/* do nothing */
  9451.  
  9452.     if(!p || !*p){
  9453.     if(full_lcc)
  9454.       *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
  9455.  
  9456.     return 0;
  9457.     }
  9458.  
  9459.     if(error != NULL)
  9460.       *error = (char *)NULL;
  9461.  
  9462.     /*
  9463.      * If we end up jumping back here because somebody else changed one of
  9464.      * our addrbooks out from underneath us, we may well leak some memory.
  9465.      * That's probably ok since this will be very rare.
  9466.      */
  9467.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  9468.     if(setjmp(addrbook_changed_unexpectedly)){
  9469.     if(full_lcc && *full_lcc)
  9470.       fs_give((void **)full_lcc);
  9471.  
  9472.     q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
  9473.     dprint(1, (debugfile,
  9474.         "RESETTING address book... build_address(%s)!\n", lcc));
  9475.     addrbook_reset();
  9476.     }
  9477.  
  9478.     bldlcc.type    = Str;
  9479.     bldlcc.arg.str = lcc;
  9480.     /*
  9481.      * Lcc is first affected_entry and fcc is second.
  9482.      * The conditional stuff for the fcc argument says to only change the
  9483.      * fcc if the fcc pointer is passed in non-null, and the To pointer
  9484.      * is also non-null.  If they are null, that means they've already been
  9485.      * entered (are sticky).  We don't affect fcc if either fcc or To has
  9486.      * been typed in.
  9487.      */
  9488.     ret_val = build_address_internal(bldlcc,
  9489.         full_lcc,
  9490.         error,
  9491.         (to_line && to_line->next && to_line->next->tptr
  9492.            && to_line->tptr) ? &fcc_local : NULL,
  9493.         &no_repo,
  9494.         (to_line && to_line->tptr) ? &to : NULL,
  9495.         1, 0);
  9496.  
  9497.     /* full_lcc is what ends up in the Lcc: line */
  9498.     if(full_lcc && *full_lcc){
  9499.     /*
  9500.      * Have to rfc1522_decode the full_to string before sending it back.
  9501.      * This should not be necessary anymore since it is being decoded
  9502.      * in build_address_internal.  Just being safe.
  9503.      */
  9504.     p = (char *)fs_get((strlen(*full_lcc) + 1) * sizeof(char));
  9505.     dummy = NULL;
  9506.     if(rfc1522_decode((unsigned char *)p, *full_lcc, &dummy)
  9507.                         == (unsigned char *)p){
  9508.         fs_give((void **)full_lcc);
  9509.         *full_lcc = p;
  9510.     }
  9511.     else
  9512.       fs_give((void **)&p);
  9513.  
  9514.     if(dummy)
  9515.       fs_give((void **)&dummy);
  9516.     }
  9517.  
  9518.     /* to is what ends up in the To: line */
  9519.     if(to && *to){
  9520.     /*
  9521.      * Have to rfc1522_decode the full_to string before sending it back.
  9522.      * This should not be necessary anymore since it is being decoded
  9523.      * in build_address_internal.  Just being safe.
  9524.      */
  9525.     p = (char *)fs_get((strlen(to) + 1) * sizeof(char));
  9526.     dummy = NULL;
  9527.     if(rfc1522_decode((unsigned char *)p, to, &dummy)
  9528.                         == (unsigned char *)p){
  9529.         fs_give((void **)&to);
  9530.         to = p;
  9531.     }
  9532.     else
  9533.       fs_give((void **)&p);
  9534.  
  9535.     if(dummy)
  9536.       fs_give((void **)&dummy);
  9537.  
  9538.     if(to_line->xtra)
  9539.       bp = (BuilderPrivate *)(*(to_line->xtra));
  9540.  
  9541.     if(bp && bp->who == BP_Lcc){
  9542.         int len;
  9543.  
  9544.         len = strlen(lcc);
  9545.         if(len >= bp->cksumlen){
  9546.         int save;
  9547.  
  9548.         save = lcc[bp->cksumlen];
  9549.         lcc[bp->cksumlen] = '\0';
  9550.         csum = line_hash(lcc);
  9551.         lcc[bp->cksumlen] = save;
  9552.         }
  9553.         else
  9554.           csum = bp->cksumval + 1;
  9555.     }
  9556.  
  9557.     if(!bp || (bp->who == BP_Lcc && csum != bp->cksumval)){
  9558.         /* replace to value */
  9559.         if(to_line->tptr)
  9560.           fs_give((void **)&to_line->tptr);
  9561.  
  9562.         to_line->tptr = to;
  9563.  
  9564.         /* put in new xtra for next time through */
  9565.         if(to_line->xtra){
  9566.         if(*(to_line->xtra))
  9567.           fs_give(to_line->xtra);
  9568.  
  9569.         /* only need an xtra if no_repo is set */
  9570.         if(no_repo){
  9571.             *(to_line->xtra)
  9572.                   = (void *)fs_get(sizeof(struct builder_private));
  9573.             bp = (BuilderPrivate *)(*(to_line->xtra));
  9574.             bp->who = BP_Lcc;
  9575.             bp->cksumlen = strlen((full_lcc && *full_lcc)
  9576.                         ? *full_lcc : "");
  9577.             bp->cksumval = line_hash((full_lcc && *full_lcc)
  9578.                         ? *full_lcc : "");
  9579.         }
  9580.         }
  9581.     }
  9582.     else
  9583.       fs_give((void **)&to);
  9584.     }
  9585.  
  9586.     if(fcc_local){
  9587.     /*
  9588.      * If xtra is set, that means fcc was set from a list
  9589.      * during some previous builder call.
  9590.      * If the current To line contains xtra as a prefix, then
  9591.      * we should leave things as they are.  In order to decide
  9592.      * that, we look at a hash value computed from the two strings.
  9593.      */
  9594.     bp = NULL;
  9595.     if(to_line->next->xtra)
  9596.       bp = (BuilderPrivate *)(*(to_line->next->xtra));
  9597.  
  9598.     if(bp && bp->who == BP_Lcc){
  9599.         int len;
  9600.  
  9601.         len = strlen(lcc);
  9602.         if(len >= bp->cksumlen){
  9603.         int save;
  9604.  
  9605.         save = lcc[bp->cksumlen];
  9606.         lcc[bp->cksumlen] = '\0';
  9607.         csum = line_hash(lcc);
  9608.         lcc[bp->cksumlen] = save;
  9609.         }
  9610.         else
  9611.           csum = bp->cksumval + 1;
  9612.     }
  9613.  
  9614.     if(!bp || (bp->who == BP_Lcc && csum != bp->cksumval)){
  9615.         /* replace fcc value */
  9616.         if(to_line->next->tptr)
  9617.           fs_give((void **)&to_line->next->tptr);
  9618.  
  9619.         to_line->next->tptr = fcc_local;
  9620.  
  9621.         /* put in new xtra for next time through */
  9622.         if(to_line->next->xtra){
  9623.         if(*(to_line->next->xtra))
  9624.           fs_give(to_line->next->xtra);
  9625.  
  9626.         /* only need an xtra if no_repo is set */
  9627.         if(no_repo){
  9628.             *(to_line->next->xtra)
  9629.                   = (void *)fs_get(sizeof(struct builder_private));
  9630.             bp = (BuilderPrivate *)(*(to_line->next->xtra));
  9631.             bp->who = BP_Lcc;
  9632.             bp->cksumlen = strlen((full_lcc && *full_lcc)
  9633.                         ? *full_lcc : "");
  9634.             bp->cksumval = line_hash((full_lcc && *full_lcc)
  9635.                         ? *full_lcc : "");
  9636.         }
  9637.         }
  9638.     }
  9639.     else
  9640.       fs_give((void **)&fcc_local);
  9641.     }
  9642.  
  9643.  
  9644.     if(error != NULL && *error == NULL)
  9645.       *error = cpystr("");
  9646.  
  9647.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  9648.     return(ret_val);
  9649. }
  9650.  
  9651.  
  9652. /*
  9653.  * This is the build_address used by the composer to check for an address
  9654.  * in the addrbook.
  9655.  *
  9656.  * Args: to      -- the passed in line to parse
  9657.  *       full_to -- Address of a pointer to return the full address in.
  9658.  *            This will be allocated here and freed by the caller.
  9659.  *       error   -- Address of a pointer to return an error message in.
  9660.  *            This will be allocated here and freed by the caller.
  9661.  *       fcc     -- Address of a pointer to return the fcc in is in
  9662.  *            fcc->tptr.  It will have already been allocated by the
  9663.  *            caller but we may free it and reallocate if we wish.
  9664.  *            Caller will free it.
  9665.  *
  9666.  * Result:  0 is returned if address was OK, 
  9667.  *         -1 if address wasn't OK.
  9668.  *
  9669.  * Side effect: Can flush addrbook entry cache entries so they need to be
  9670.  * re-fetched afterwords.
  9671.  */
  9672. int
  9673. build_address(to, full_to, error, fcc)
  9674.     char     *to,
  9675.         **full_to,
  9676.         **error;
  9677.     BUILDER_ARG     *fcc;
  9678. {
  9679.     char   *p;
  9680.     int     ret_val, no_repo = 0;
  9681.     BuildTo bldto;
  9682.     BuilderPrivate *bp = NULL;
  9683.     char   *fcc_local = NULL, *dummy = NULL;
  9684.     long    csum;
  9685.     jmp_buf save_jmp_buf;
  9686.  
  9687.     dprint(2, (debugfile, "- build_address - (%s)\n", to ? to : "nul"));
  9688.  
  9689.     /* check to see if to string is empty to avoid work */
  9690.     for(p = to; p && *p && isspace((unsigned char)*p); p++)
  9691.       ;/* do nothing */
  9692.  
  9693.     if(!p || !*p){
  9694.     if(full_to)
  9695.       *full_to = cpystr(to ? to : "");  /* because pico does a strcmp() */
  9696.  
  9697.     return 0;
  9698.     }
  9699.  
  9700.     if(full_to != NULL)
  9701.       *full_to = (char *)NULL;
  9702.  
  9703.     if(error != NULL)
  9704.       *error = (char *)NULL;
  9705.  
  9706.     /*
  9707.      * If we end up jumping back here because somebody else changed one of
  9708.      * our addrbooks out from underneath us, we may well leak some memory.
  9709.      * That's probably ok since this will be very rare.
  9710.      *
  9711.      * The reason for the memcpy of the jmp_buf is that we may actually
  9712.      * be indirectly calling this function from within the address book.
  9713.      * For example, we may be in the address book screen and then run
  9714.      * the ComposeTo command which puts us in the composer, then we call
  9715.      * build_address from there which resets addrbook_changed_unexpectedly.
  9716.      * Once we leave build_address we need to reset addrbook_changed_un...
  9717.      * because this position on the stack will no longer be valid.
  9718.      * Same is true of the other setjmp's in this file which are wrapped
  9719.      * in memcpy calls.
  9720.      */
  9721.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  9722.     if(setjmp(addrbook_changed_unexpectedly)){
  9723.     if(full_to && *full_to)
  9724.       fs_give((void **)full_to);
  9725.  
  9726.     q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
  9727.     dprint(1, (debugfile,
  9728.         "RESETTING address book... build_address(%s)!\n", to));
  9729.     addrbook_reset();
  9730.     }
  9731.  
  9732.     bldto.type    = Str;
  9733.     bldto.arg.str = to;
  9734.     ret_val = build_address_internal(bldto, full_to, error,
  9735.             fcc ? &fcc_local : NULL, &no_repo, NULL, 1, 0);
  9736.  
  9737.     /*
  9738.      * Have to rfc1522_decode the full_to string before sending it back.
  9739.      * This should not be necessary anymore since it is being decoded
  9740.      * in build_address_internal.  Just being safe.
  9741.      */
  9742.     if(full_to && *full_to ){
  9743.     p = (char *)fs_get((strlen(*full_to) + 1) * sizeof(char));
  9744.     if(rfc1522_decode((unsigned char *)p, *full_to, &dummy)
  9745.                             == (unsigned char *)p){
  9746.         fs_give((void **)full_to);
  9747.         *full_to = p;
  9748.     }
  9749.     else
  9750.       fs_give((void **)&p);
  9751.  
  9752.     if(dummy)
  9753.       fs_give((void **)&dummy);
  9754.     }
  9755.  
  9756.     if(fcc_local){
  9757.     /*
  9758.      * If fcc->xtra is set, that means fcc was set from a list
  9759.      * during some previous builder call.
  9760.      * If the current To line contains the old expansion as a prefix, then
  9761.      * we should leave things as they are.  In order to decide that,
  9762.      * we look at a hash value computed from the strings.
  9763.      */
  9764.     if(fcc->xtra)
  9765.       bp = (BuilderPrivate *)(*(fcc->xtra));
  9766.  
  9767.     if(bp && bp->who == BP_To){
  9768.         int len;
  9769.  
  9770.         len = strlen(to);
  9771.         if(len >= bp->cksumlen){
  9772.         int save;
  9773.  
  9774.         save = to[bp->cksumlen];
  9775.         to[bp->cksumlen] = '\0';
  9776.         csum = line_hash(to);
  9777.         to[bp->cksumlen] = save;
  9778.         }
  9779.         else
  9780.           csum = bp->cksumval + 1;  /* something not equal to cksumval */
  9781.     }
  9782.  
  9783.     if(!bp || (bp->who == BP_To && csum != bp->cksumval)){
  9784.         /* replace fcc value */
  9785.         if(fcc->tptr)
  9786.           fs_give((void **)&fcc->tptr);
  9787.  
  9788.         fcc->tptr = fcc_local;
  9789.  
  9790.         /* put in new xtra for next time through */
  9791.         if(fcc->xtra){
  9792.         if(*(fcc->xtra))
  9793.           fs_give(fcc->xtra);
  9794.  
  9795.         /* only need an xtra if no_repo is true */
  9796.         if(no_repo){
  9797.             *(fcc->xtra)
  9798.                 = (void *)fs_get(sizeof(struct builder_private));
  9799.             bp = (BuilderPrivate *)(*(fcc->xtra));
  9800.             bp->who = BP_To;
  9801.             bp->cksumlen = strlen(((full_to && *full_to)
  9802.                         ? *full_to : ""));
  9803.             bp->cksumval = line_hash(((full_to && *full_to)
  9804.                         ? *full_to : ""));
  9805.         }
  9806.         }
  9807.     }
  9808.     else
  9809.       fs_give((void **)&fcc_local);  /* unused in this case */
  9810.     }
  9811.  
  9812.     /* This is so pico will erase the old message */
  9813.     if(error != NULL && *error == NULL)
  9814.       *error = cpystr("");
  9815.  
  9816.     memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
  9817.     return(ret_val);
  9818. }
  9819.  
  9820.  
  9821. #define FCC_SET    1    /* Fcc was set */
  9822. #define LCC_SET    2    /* Lcc was set */
  9823. #define FCC_NOREPO 4    /* Fcc was set in a non-reproducible way */
  9824. #define LCC_NOREPO 8    /* Lcc was set in a non-reproducible way */
  9825. /*
  9826.  * Given an address, expand it based on address books, local domain, etc.
  9827.  * This will open addrbooks if needed before checking (actually one of
  9828.  * its children will open them).
  9829.  *
  9830.  * Args: to       -- The given address to expand (see the description
  9831.  *            in expand_address)
  9832.  *       full_to  -- Returned value after parsing to.
  9833.  *       error    -- This gets pointed at error message, if any
  9834.  *       fcc      -- Returned value of fcc for first addr in to
  9835.  *       no_repo  -- Returned value, set to 1 if the fcc or lcc we're
  9836.  *            returning is not reproducible from the expanded
  9837.  *            address.  That is, if we were to run
  9838.  *            build_address_internal again on the resulting full_to,
  9839.  *            we wouldn't get back the fcc again.  For example,
  9840.  *            if we expand a list and use the list fcc from the
  9841.  *            addrbook, the full_to no longer contains the
  9842.  *            information that this was originally list foo.
  9843.  *       save_and_restore -- restore addrbook state when done
  9844.  *
  9845.  * Result:  0 is returned if address was OK, 
  9846.  *         -1 if address wasn't OK.
  9847.  * The address is expanded, fully-qualified, and personal name added.
  9848.  *
  9849.  * Input may have more than one address separated by commas.
  9850.  *
  9851.  * Side effect: Can flush addrbook entry cache entries so they need to be
  9852.  * re-fetched afterwords.
  9853.  */
  9854. int
  9855. build_address_internal(to, full_to, error, fcc, no_repo, lcc, save_and_restore,
  9856.                simple_verify)
  9857.     BuildTo to;
  9858.     char  **full_to,
  9859.       **error,
  9860.       **fcc,
  9861.       **lcc;
  9862.     int    *no_repo;
  9863.     int     save_and_restore, simple_verify;
  9864. {
  9865.     ADDRESS *a;
  9866.     int      loop, i;
  9867.     int      tried_route_addr_hack = 0;
  9868.     int      did_set = 0;
  9869.     char    *tmp = NULL;
  9870.     SAVE_STATE_S  state;
  9871.     PerAddrBook  *pab;
  9872.  
  9873.     dprint(8, (debugfile, "- build_address_internal -  (%s)\n",
  9874.     (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
  9875.              : (to.arg.abe->nickname ? to.arg.abe->nickname
  9876.                         : "no nick")));
  9877.  
  9878.     init_ab_if_needed();
  9879.     if(save_and_restore)
  9880.       save_state(&state);
  9881.  
  9882. start:
  9883.     loop = 0;
  9884.     ps_global->c_client_error[0] = '\0';
  9885.  
  9886.     a = expand_address(to, ps_global->maildomain,
  9887.                F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
  9888.              ? ps_global->maildomain : NULL,
  9889.                &loop, fcc, &did_set, lcc, error,
  9890.                0, simple_verify);
  9891.  
  9892.     /*
  9893.      * If the address is a route-addr, expand_address() will have rejected
  9894.      * it unless it was enclosed in brackets, since route-addrs can't stand
  9895.      * alone.  Try it again with brackets.  We should really be checking
  9896.      * each address in the list of addresses instead of assuming there is
  9897.      * only one address, but we don't want to have this function know
  9898.      * all about parsing rfc822 addrs.
  9899.      */
  9900.     if(!tried_route_addr_hack &&
  9901.         ps_global->c_client_error[0] != '\0' &&
  9902.     ((to.type == Str && to.arg.str && to.arg.str[0] == '@') ||
  9903.      (to.type == Abe && to.arg.abe->tag == Single &&
  9904.                  to.arg.abe->addr.addr[0] == '@'))){
  9905.     BuildTo      bldto;
  9906.  
  9907.     tried_route_addr_hack++;
  9908.  
  9909.     tmp = (char *)fs_get((size_t)(MAX_ADDR_FIELD + 3));
  9910.  
  9911.     /* add brackets to whole thing */
  9912.     if(to.type == Str)
  9913.       strcat(strcat(strcpy(tmp, "<"), to.arg.str), ">");
  9914.     else
  9915.       strcat(strcat(strcpy(tmp, "<"), to.arg.abe->addr.addr), ">");
  9916.  
  9917.     loop = 0;
  9918.     ps_global->c_client_error[0] = '\0';
  9919.  
  9920.     bldto.type    = Str;
  9921.     bldto.arg.str = tmp;
  9922.  
  9923.     if(a)
  9924.       mail_free_address(&a);
  9925.  
  9926.     /* try it */
  9927.     a = expand_address(bldto, ps_global->maildomain,
  9928.                F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
  9929.                  ? ps_global->maildomain : NULL,
  9930.                &loop, fcc, &did_set, lcc, error,
  9931.                0, simple_verify);
  9932.  
  9933.     /* if no error this time, use it */
  9934.     if(ps_global->c_client_error[0] == '\0'){
  9935.         if(save_and_restore)
  9936.           restore_state(&state);
  9937.  
  9938.         /*
  9939.          * Clear references so that Addrbook Entry caching in adrbklib.c
  9940.          * is allowed to throw them out of cache.
  9941.          */
  9942.         for(i = 0; i < as.n_addrbk; i++){
  9943.         pab = &as.adrbks[i];
  9944.         if(pab->ostatus == Open || pab->ostatus == NoDisplay)
  9945.           adrbk_clearrefs(pab->address_book);
  9946.         }
  9947.  
  9948.         goto ok;
  9949.     }
  9950.     else  /* go back and use what we had before, so we get the error */
  9951.       goto start;
  9952.     }
  9953.  
  9954.     if(save_and_restore)
  9955.       restore_state(&state);
  9956.  
  9957.     /*
  9958.      * Clear references so that Addrbook Entry caching in adrbklib.c
  9959.      * is allowed to throw them out of cache.
  9960.      */
  9961.     for(i = 0; i < as.n_addrbk; i++){
  9962.     pab = &as.adrbks[i];
  9963.     if(pab->ostatus == Open || pab->ostatus == NoDisplay)
  9964.       adrbk_clearrefs(pab->address_book);
  9965.     }
  9966.  
  9967.     if(ps_global->c_client_error[0] != '\0'){
  9968.         /* Parse error.  Return string as is and error message */
  9969.     if(full_to){
  9970.         if(to.type == Str)
  9971.           *full_to = cpystr(to.arg.str);
  9972.         else{
  9973.             if(to.arg.abe->nickname && to.arg.abe->nickname[0])
  9974.               *full_to = cpystr(to.arg.abe->nickname);
  9975.             else if(to.arg.abe->tag == Single)
  9976.               *full_to = cpystr(to.arg.abe->addr.addr);
  9977.             else
  9978.               *full_to = cpystr("");
  9979.         }
  9980.     }
  9981.  
  9982.         if(error != NULL)
  9983.           *error = cpystr(ps_global->c_client_error);
  9984.  
  9985.         dprint(2, (debugfile,
  9986.         "build_address_internal returning parse error: %s\n",
  9987.                    ps_global->c_client_error));
  9988.     if(a)
  9989.       mail_free_address(&a);
  9990.  
  9991.     if(tmp)
  9992.       fs_give((void **)&tmp);
  9993.  
  9994.         return -1;
  9995.     }
  9996.  
  9997.     /*
  9998.      * If there's a loop in the addressbook, we modify the address and
  9999.      * send an error back, but we still return 0.
  10000.      */
  10001. ok:
  10002.     if(loop && error != NULL)
  10003.       *error = cpystr("Loop or Duplicate detected in addressbook!");
  10004.  
  10005.  
  10006.     if(full_to){
  10007.     *full_to = (char *)fs_get((size_t)est_size(a));
  10008.     *full_to[0] = '\0';
  10009.     /*
  10010.      * Assume that quotes surrounding the whole personal name are
  10011.      * not meant to be literal quotes.  That is, the name
  10012.      * "Joe College, PhD." is quoted so that we won't do the
  10013.      * switcheroo of Last, First, not so that the quotes will be
  10014.      * literal.  Rfc822_write_address will put the quotes back if they
  10015.      * are needed, so Joe College would end up looking like
  10016.      * "Joe College, PhD." <joe@somewhere.edu> but not like
  10017.      * "\"Joe College, PhD.\"" <joe@somewhere.edu>.
  10018.      */
  10019.     strip_personal_quotes(a);
  10020.     rfc822_write_address(*full_to, a);
  10021.     }
  10022.  
  10023.     if(no_repo && (did_set & FCC_NOREPO || did_set & LCC_NOREPO))
  10024.       *no_repo = 1;
  10025.  
  10026.     /*
  10027.      * The condition in the leading if means that addressbook fcc's
  10028.      * override the fcc-rule (because did_set will be set).
  10029.      */
  10030.     if(fcc && !(did_set & FCC_SET)){
  10031.     char *fcc_got = NULL;
  10032.  
  10033.     if((ps_global->fcc_rule == FCC_RULE_LAST
  10034.         || ps_global->fcc_rule == FCC_RULE_CURRENT)
  10035.        && strcmp(fcc_got = get_fcc(NULL), ps_global->VAR_DEFAULT_FCC)){
  10036.         if(*fcc)
  10037.           fs_give((void **)fcc);
  10038.         
  10039.         *fcc = cpystr(fcc_got);
  10040.     }
  10041.     else if(a && a->host){ /* not group syntax */
  10042.         if(*fcc)
  10043.           fs_give((void **)fcc);
  10044.  
  10045.         if(!tmp)
  10046.           tmp = (char *)fs_get((size_t)200);
  10047.  
  10048.         if((ps_global->fcc_rule == FCC_RULE_RECIP ||
  10049.         ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
  10050.           get_uname(a ? a->mailbox : NULL, tmp, 200))
  10051.           *fcc = cpystr(tmp);
  10052.         else
  10053.           *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
  10054.     }
  10055.     else{ /* first addr is group syntax */
  10056.         if(!*fcc)
  10057.           *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
  10058.         /* else, leave it alone */
  10059.     }
  10060.  
  10061.     if(fcc_got)
  10062.       fs_give((void **)&fcc_got);
  10063.     }
  10064.  
  10065.     if(a)
  10066.       mail_free_address(&a);
  10067.  
  10068.     if(tmp)
  10069.       fs_give((void **)&tmp);
  10070.  
  10071.     return 0;
  10072. }
  10073.  
  10074.  
  10075. /*
  10076.  * Expand an address string against the address books, local names, and domain.
  10077.  *
  10078.  * Args:  to      -- this is either an address string to parse (one or more
  10079.  *              address strings separated by commas) or it is an
  10080.  *              AdrBk_Entry, in which case it refers to a single addrbook
  10081.  *              entry.  If it is an abe, then it is treated the same as
  10082.  *              if the nickname of this entry was passed in and we
  10083.  *              looked it up in the addrbook, except that it doesn't
  10084.  *              actually have to have a non-null nickname.
  10085.  *     userdomain -- domain the user is in
  10086.  *    localdomain -- domain of the password file (usually same as userdomain)
  10087.  *  loop_detected -- pointer to an int we set if we detect a loop in the
  10088.  *             address books (or a duplicate in a list)
  10089.  *       fcc      -- Returned value of fcc for first addr in a_string
  10090.  *       did_set  -- expand_address set the fcc (need this in case somebody
  10091.  *                     sets fcc explicitly to a value equal to default-fcc)
  10092.  *  simple_verify -- don't add list full names or expand 2nd level lists
  10093.  *
  10094.  * Result: An adrlist of expanded addresses is returned
  10095.  *
  10096.  * If the localdomain is NULL, then no lookup against the password file will
  10097.  * be done.
  10098.  */
  10099. ADDRESS *
  10100. expand_address(to, userdomain, localdomain, loop_detected, fcc, did_set,
  10101.     lcc, error, recursing, simple_verify)
  10102.     BuildTo  to;
  10103.     char    *userdomain,
  10104.         *localdomain;
  10105.     int     *loop_detected;
  10106.     char   **fcc;
  10107.     int     *did_set;
  10108.     char   **lcc;
  10109.     char   **error;
  10110.     int      recursing, simple_verify;
  10111. {
  10112.     size_t       domain_length, length;
  10113.     ADDRESS     *adr, *a, *a_tail, *adr2, *a2, *a_temp;
  10114.     AdrBk_Entry *abe, *abe2;
  10115.     char        *list, *l1, **l2;
  10116.     char        *tmp_a_string, *q, *dummy;
  10117.     BuildTo      bldto;
  10118.     static char *fakedomain;
  10119.  
  10120.     dprint(9, (debugfile, "- expand_address -  (%s)\n",
  10121.     (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
  10122.              : (to.arg.abe->nickname ? to.arg.abe->nickname
  10123.                         : "no nick")));
  10124.  
  10125.     /*
  10126.      * We use the domain "@" to detect an unqualified address.  If it comes
  10127.      * back from rfc822_parse_adrlist with the host part set to "@", then
  10128.      * we know it must have been unqualified (so we should look it up in the
  10129.      * addressbook).  Later, we also use a c-client hack.  If an ADDRESS has
  10130.      * a host part that begins with @ then rfc822_write_address()
  10131.      * will write only the local part and leave off the @domain part.
  10132.      *
  10133.      * We also malloc enough space here so that we can strcpy over host below.
  10134.      */
  10135.     if(!recursing){
  10136.     domain_length = max(localdomain!=NULL ? strlen(localdomain) : (size_t)0,
  10137.                 userdomain!=NULL ? strlen(userdomain) : (size_t)0);
  10138.     fakedomain = (char *)fs_get(domain_length + 1);
  10139.     memset((void *)fakedomain, '@', domain_length);
  10140.     fakedomain[domain_length] = '\0';
  10141.     }
  10142.  
  10143.     adr = NULL;
  10144.  
  10145.     if(to.type == Str){
  10146.     /* rfc822_parse_adrlist feels free to destroy input so send copy */
  10147.     tmp_a_string = cpystr(to.arg.str);
  10148.     /* remove trailing comma */
  10149.     for(q = tmp_a_string + strlen(tmp_a_string) - 1;
  10150.         q >= tmp_a_string && (*q == SPACE || *q == ',');
  10151.         q--)
  10152.       *q = '\0';
  10153.  
  10154.     rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
  10155.     fs_give((void **)&tmp_a_string);
  10156.     }
  10157.     else{
  10158.     /* if we've already looked it up, fake an adr */
  10159.     adr = mail_newaddr();
  10160.     adr->mailbox = cpystr(to.arg.abe->nickname);
  10161.     adr->host    = cpystr(fakedomain);
  10162.     }
  10163.  
  10164.     for(a = adr, a_tail = adr; a;){
  10165.  
  10166.     /* start or end of c-client group syntax */
  10167.     if(!a->host){
  10168.             a_tail = a;
  10169.             a      = a->next;
  10170.         continue;
  10171.     }
  10172.         else if(a->host[0] != '@'){
  10173.             /* Already fully qualified hostname */
  10174.             a_tail = a;
  10175.             a      = a->next;
  10176.         }
  10177.         else{
  10178.             /*
  10179.              * Hostname is "@" indicating name wasn't qualified.
  10180.              * Need to look up in address book, and the password file.
  10181.              * If no match then fill in the local domain for host.
  10182.              */
  10183.         if(to.type == Str)
  10184.               abe = adrbk_lookup_with_opens_by_nick(a->mailbox,
  10185.                             !recursing,
  10186.                             (int *)NULL, -1);
  10187.         else
  10188.               abe = to.arg.abe;
  10189.  
  10190.             if(abe == NULL){
  10191.         /* normal case */
  10192.             if(F_OFF(F_COMPOSE_REJECTS_UNQUAL, ps_global)){
  10193.             if(localdomain != NULL && a->personal == NULL){
  10194.             /* lookup in passwd file for local full name */
  10195.             a->personal = local_name_lookup(a->mailbox); 
  10196.  
  10197.             strcpy(a->host, a->personal == NULL ? userdomain :
  10198.                                   localdomain);
  10199.             }
  10200.             else
  10201.               strcpy(a->host, userdomain);
  10202.         }
  10203.         else{
  10204.             if(error != NULL && *error == (char *)NULL){
  10205.             char ebuf[200];
  10206.  
  10207.             sprintf(ebuf, "%s not in addressbook", a->mailbox);
  10208.             *error = cpystr(ebuf);
  10209.             }
  10210.         }
  10211.  
  10212.                 /*--- Move to next address in list -----*/
  10213.                 a_tail = a;
  10214.                 a = a->next;
  10215.  
  10216.             }
  10217.         /* expand first list, but not others if simple_verify */
  10218.         else if(abe->tag == List && simple_verify && recursing){
  10219.                 /*--- Move to next address in list -----*/
  10220.                 a_tail = a;
  10221.                 a = a->next;
  10222.         }
  10223.         else{
  10224.                 /*
  10225.                  * There was a match in the address book.  We have to do a lot
  10226.                  * here because the item from the address book might be a 
  10227.                  * distribution list.  Treat the string just like an address
  10228.                  * passed in to parse and recurse on it.  Then combine
  10229.                  * the personal names from address book.  Lastly splice
  10230.                  * result into address list being processed
  10231.                  */
  10232.  
  10233.         /* first addr in list and fcc needs to be filled in */
  10234.         if(a == adr && fcc && !(*did_set & FCC_SET)){
  10235.             /*
  10236.              * Easy case for fcc.  This is a nickname that has
  10237.              * an fcc associated with it.
  10238.              */
  10239.             if(abe->fcc && abe->fcc[0]){
  10240.             if(*fcc)
  10241.               fs_give((void **)fcc);
  10242.  
  10243.             if(!strcmp(abe->fcc, "\"\""))
  10244.               *fcc = cpystr("");
  10245.             else
  10246.               *fcc = cpystr(abe->fcc);
  10247.  
  10248.             /*
  10249.              * After we expand the list, we no longer remember
  10250.              * that it came from this address book entry, so
  10251.              * we wouldn't be able to set the fcc again based
  10252.              * on the result.  This tells our caller to remember
  10253.              * that for us.
  10254.              */
  10255.             *did_set |= (FCC_SET | FCC_NOREPO);
  10256.             }
  10257.             /*
  10258.              * else if fcc-rule=fcc-by-nickname, use that
  10259.              */
  10260.             else if(abe->nickname && abe->nickname[0] &&
  10261.                 (ps_global->fcc_rule == FCC_RULE_NICK ||
  10262.                  ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
  10263.             if(*fcc)
  10264.               fs_give((void **)fcc);
  10265.  
  10266.             *fcc = cpystr(abe->nickname);
  10267.             /*
  10268.              * After we expand the list, we no longer remember
  10269.              * that it came from this address book entry, so
  10270.              * we wouldn't be able to set the fcc again based
  10271.              * on the result.  This tells our caller to remember
  10272.              * that for us.
  10273.              */
  10274.             *did_set |= (FCC_SET | FCC_NOREPO);
  10275.             }
  10276.         }
  10277.  
  10278.         /* lcc needs to be filled in */
  10279.         if(a == adr  &&
  10280.             lcc      &&
  10281.            (!*lcc || !**lcc)){
  10282.             ADDRESS *atmp;
  10283.             char    *tmp;
  10284.  
  10285.             /* return fullname for To line */
  10286.             if(abe->fullname && *abe->fullname){
  10287.             if(*lcc)
  10288.               fs_give((void **)lcc);
  10289.  
  10290.             atmp = mail_newaddr();
  10291.             dummy = NULL;
  10292.             q = (char *)rfc1522_decode((unsigned char *)
  10293.                 (tmp_20k_buf+10000), abe->fullname, &dummy);
  10294.             if(dummy)
  10295.               fs_give((void **)&dummy);
  10296.  
  10297.             atmp->mailbox = cpystr(q);
  10298.             tmp = (char *)fs_get((size_t)est_size(atmp));
  10299.             tmp[0] = '\0';
  10300.             /* write the phrase with quoting */
  10301.             rfc822_write_address(tmp, atmp);
  10302.             *lcc = (char *)fs_get((size_t)(strlen(tmp)+1+1));
  10303.             strcpy(*lcc, tmp);
  10304.             strcat(*lcc, ";");
  10305.             mail_free_address(&atmp);
  10306.             fs_give((void **)&tmp);
  10307.             *did_set |= (LCC_SET | LCC_NOREPO);
  10308.             }
  10309.         }
  10310.  
  10311.                 if(recursing && abe->referenced){
  10312.                      /*---- caught an address loop! ----*/
  10313.                     fs_give(((void **)&a->host));
  10314.             (*loop_detected)++;
  10315.                     a->host = cpystr("***address-loop-in-addressbooks***");
  10316.                     continue;
  10317.                 }
  10318.  
  10319.                 abe->referenced++;   /* For address loop detection */
  10320.                 if(abe->tag == List){
  10321.                     length = 0;
  10322.                     for(l2 = abe->addr.list; *l2; l2++)
  10323.                         length += (strlen(*l2) + 1);
  10324.  
  10325.                     list = (char *)fs_get(length + 1);
  10326.             list[0] = '\0';
  10327.                     l1 = list;
  10328.                     for(l2 = abe->addr.list; *l2; l2++){
  10329.                         if(l1 != list)
  10330.                           *l1++ = ',';
  10331.                         strcpy(l1, *l2);
  10332.                         l1 += strlen(l1);
  10333.                     }
  10334.  
  10335.             bldto.type    = Str;
  10336.             bldto.arg.str = list;
  10337.                     adr2 = expand_address(bldto, userdomain, localdomain,
  10338.                       loop_detected, fcc, did_set,
  10339.                       lcc, error, 1, simple_verify);
  10340.                     fs_give((void **)&list);
  10341.                 }
  10342.                 else if(abe->tag == Single){
  10343.                     if(strucmp(abe->addr.addr, a->mailbox)){
  10344.             bldto.type    = Str;
  10345.             bldto.arg.str = abe->addr.addr;
  10346.                         adr2 = expand_address(bldto, userdomain,
  10347.                           localdomain, loop_detected,
  10348.                           fcc, did_set, lcc,
  10349.                           error, 1, simple_verify);
  10350.                     }
  10351.             else{
  10352.                         /*
  10353.              * A loop within plain single entry is ignored.
  10354.              * Set up so later code thinks we expanded.
  10355.              */
  10356.                         adr2          = mail_newaddr();
  10357.                         adr2->mailbox = cpystr(abe->addr.addr);
  10358.                         adr2->host    = cpystr(userdomain);
  10359.                         adr2->adl     = cpystr(a->adl);
  10360.                     }
  10361.                 }
  10362.  
  10363.                 abe->referenced--;  /* Janet Jackson <janet@dialix.oz.au> */
  10364.                 if(adr2 == NULL){
  10365.                     /* expanded to nothing, hack out of list */
  10366.                     a_temp = a;
  10367.                     if(a == adr){
  10368.                         adr    = a->next;
  10369.                         a      = adr;
  10370.                         a_tail = adr;
  10371.                     }
  10372.             else{
  10373.                         a_tail->next = a->next;
  10374.                         a            = a->next;
  10375.                     }
  10376.  
  10377.             a_temp->next = NULL;  /* So free won't do whole list */
  10378.                     mail_free_address(&a_temp);
  10379.                     continue;
  10380.                 }
  10381.  
  10382.                 /*
  10383.                  * Personal names:  If the expanded address has a personal
  10384.                  * name and the address book entry is a list with a fullname,
  10385.          * tack the full name from the address book on in front.
  10386.                  * This mainly occurs with a distribution list where the
  10387.                  * list has a full name, and the first person in the list also
  10388.                  * has a full name.
  10389.          *
  10390.          * This algorithm doesn't work very well if lists are
  10391.          * included within lists, but it's not clear what would
  10392.          * be better.
  10393.                  */
  10394.         if(abe->fullname && abe->fullname[0]){
  10395.             if(adr2->personal && adr2->personal[0]){
  10396.             if(abe->tag == List){
  10397.                 /* combine list name and existing name */
  10398.                 char *name;
  10399.  
  10400.                 if(!simple_verify){
  10401.                 name = (char *)fs_get(strlen(adr2->personal) +
  10402.                           strlen(abe->fullname) + 5);
  10403.                 dummy = NULL;
  10404.                 q = (char *)rfc1522_decode((unsigned char *)
  10405.                     (tmp_20k_buf+10000), abe->fullname, &dummy);
  10406.                 if(dummy)
  10407.                   fs_give((void **)&dummy);
  10408.  
  10409.                 sprintf(name, "%s -- %s", q, adr2->personal);
  10410.                 fs_give((void **)&adr2->personal);
  10411.                 adr2->personal = name;
  10412.                 }
  10413.             }
  10414.             else{
  10415.                 /* replace with nickname fullname */
  10416.                 fs_give((void **)&adr2->personal);
  10417.                 dummy = NULL;
  10418.                 q = (char *)rfc1522_decode((unsigned char *)
  10419.                 (tmp_20k_buf+10000), abe->fullname, &dummy);
  10420.                 if(dummy)
  10421.                   fs_give((void **)&dummy);
  10422.  
  10423.                 adr2->personal =
  10424.                 cpystr(adrbk_formatname(q));
  10425.             }
  10426.             }
  10427.             else{
  10428.             if(abe->tag != List || !simple_verify){
  10429.                 if(adr2->personal)
  10430.                   fs_give((void **)&adr2->personal);
  10431.  
  10432.                 dummy = NULL;
  10433.                 q = (char *)rfc1522_decode((unsigned char *)
  10434.                 (tmp_20k_buf+10000), abe->fullname, &dummy);
  10435.                 if(dummy)
  10436.                   fs_give((void **)&dummy);
  10437.  
  10438.                 adr2->personal = cpystr(adrbk_formatname(q));
  10439.             }
  10440.             }
  10441.         }
  10442.  
  10443.                 /* splice new list into old list and remove replaced addr */
  10444.                 for(a2 = adr2; a2->next != NULL; a2 = a2->next)
  10445.           ;/* do nothing */
  10446.  
  10447.                 a2->next = a->next;
  10448.                 if(a == adr)
  10449.                   adr    = adr2;
  10450.         else
  10451.                   a_tail->next = adr2;
  10452.  
  10453.                 /* advance to next item, and free replaced ADDRESS */
  10454.                 a_tail       = a2;
  10455.                 a_temp       = a;
  10456.                 a            = a->next;
  10457.                 a_temp->next = NULL;  /* So free won't do whole list */
  10458.                 mail_free_address(&a_temp);
  10459.             }
  10460.  
  10461.         }
  10462.  
  10463.     if((a_tail == adr && fcc && !(*did_set & FCC_SET))
  10464.        || !a_tail->personal)
  10465.       /*
  10466.        * This looks for the addressbook entry that matches the given
  10467.        * address.  It looks through all the addressbooks
  10468.        * looking for an exact match and then returns that entry.
  10469.        */
  10470.       abe2 = addr_to_abe(a_tail);
  10471.     else
  10472.       abe2 = NULL;
  10473.  
  10474.     /*
  10475.      * If there is no personal name yet but we found the address in
  10476.      * an address book, then we take the fullname from that address
  10477.      * book entry and use it.  One consequence of this is that if I
  10478.      * have an address book entry with address hubert@cac.washington.edu
  10479.      * and a fullname of Steve Hubert, then there is no way I can
  10480.      * send mail to hubert@cac.washington.edu without having the
  10481.      * personal name filled in for me.
  10482.      */
  10483.     if(!a_tail->personal && abe2 && abe2->fullname && abe2->fullname[0]){
  10484.         dummy = NULL;
  10485.         q = (char *)rfc1522_decode((unsigned char *)(tmp_20k_buf+10000),
  10486.                        abe2->fullname, &dummy);
  10487.         if(dummy)
  10488.           fs_give((void **)&dummy);
  10489.  
  10490.         a_tail->personal = cpystr(adrbk_formatname(q));
  10491.     }
  10492.  
  10493.     /* if it's first addr in list and fcc hasn't been set yet */
  10494.     if(a_tail == adr && fcc && !(*did_set & FCC_SET)){
  10495.         if(abe2 && abe2->fcc && abe2->fcc[0]){
  10496.         if(*fcc)
  10497.           fs_give((void **)fcc);
  10498.  
  10499.         if(!strcmp(abe2->fcc, "\"\""))
  10500.           *fcc = cpystr("");
  10501.         else
  10502.           *fcc = cpystr(abe2->fcc);
  10503.  
  10504.         *did_set |= FCC_SET;
  10505.         }
  10506.         else if(abe2 && abe2->nickname && abe2->nickname[0] &&
  10507.             (ps_global->fcc_rule == FCC_RULE_NICK ||
  10508.              ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
  10509.         if(*fcc)
  10510.           fs_give((void **)fcc);
  10511.  
  10512.         *fcc = cpystr(abe2->nickname);
  10513.         *did_set |= FCC_SET;
  10514.         }
  10515.     }
  10516.  
  10517.     /*
  10518.      * Lcc needs to be filled in.
  10519.      * Bug: if ^T select was used to put the list in the lcc field, then
  10520.      * the list will have been expanded already and the fullname for
  10521.      * the list will be mixed with the initial fullname in the list,
  10522.      * and we don't have anyway to tell them apart.  We could look for
  10523.      * the --.  We could change expand_address so it doesn't combine
  10524.      * those two addresses.
  10525.      */
  10526.     if(adr &&
  10527.         a_tail == adr  &&
  10528.         lcc      &&
  10529.        (!*lcc || !**lcc)){
  10530.         if(adr->personal){
  10531.         ADDRESS *atmp;
  10532.         char    *tmp;
  10533.  
  10534.         if(*lcc)
  10535.           fs_give((void **)lcc);
  10536.  
  10537.         atmp = mail_newaddr();
  10538.         atmp->mailbox = cpystr(adr->personal);
  10539.         tmp = (char *)fs_get((size_t)est_size(atmp));
  10540.         tmp[0] = '\0';
  10541.         /* write the phrase with quoting */
  10542.         rfc822_write_address(tmp, atmp);
  10543.         *lcc = (char *)fs_get((size_t)(strlen(tmp)+1+1));
  10544.         strcpy(*lcc, tmp);
  10545.         strcat(*lcc, ";");
  10546.         mail_free_address(&atmp);
  10547.         fs_give((void **)&tmp);
  10548.         *did_set |= (LCC_SET | LCC_NOREPO);
  10549.         }
  10550.     }
  10551.     }
  10552.  
  10553.     if(!recursing)
  10554.       fs_give((void **)&fakedomain);
  10555.  
  10556.     return(adr);
  10557. }
  10558.  
  10559.  
  10560. /*
  10561.  * Run through the adrlist "adr" and strip off any enclosing quotes
  10562.  * around personal names.  That is, change "Joe L. Normal" to
  10563.  * Joe L. Normal.
  10564.  */
  10565. void
  10566. strip_personal_quotes(adr)
  10567.     ADDRESS *adr;
  10568. {
  10569.     int   len;
  10570.     register char *p, *q;
  10571.  
  10572.     while(adr){
  10573.     if(adr->personal){
  10574.         len = strlen(adr->personal);
  10575.         if(len > 1
  10576.            && adr->personal[0] == '"'
  10577.            && adr->personal[len-1] == '"'){
  10578.         adr->personal[len-1] = '\0';
  10579.         p = adr->personal;
  10580.         q = p + 1;
  10581.         while(*p++ = *q++)
  10582.           ;
  10583.         }
  10584.     }
  10585.  
  10586.     adr = adr->next;
  10587.     }
  10588. }
  10589.  
  10590.  
  10591. /*
  10592.  * Given an address, try to find the first nickname that goes with it.
  10593.  * Copies that nickname into the passed in buffer, which is assumed to
  10594.  * be at least MAX_NICKNAME+1 in length.  Returns NULL if it can't be found,
  10595.  * else it returns a pointer to the buffer.
  10596.  */
  10597. char *
  10598. get_nickname_from_addr(adr, buffer)
  10599.     ADDRESS *adr;
  10600.     char    *buffer;
  10601. {
  10602.     AdrBk_Entry *abe;
  10603.     char        *ret = NULL;
  10604.     SAVE_STATE_S state;
  10605.     jmp_buf     save_jmp_buf;
  10606.  
  10607.     state.savep = NULL;
  10608.     state.stp = NULL;
  10609.     state.dlc_to_warp_to = NULL;
  10610.  
  10611.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  10612.     if(setjmp(addrbook_changed_unexpectedly)){
  10613.     if(state.savep)
  10614.       fs_give((void **)&(state.savep));
  10615.     if(state.stp)
  10616.       fs_give((void **)&(state.stp));
  10617.     if(state.dlc_to_warp_to)
  10618.       fs_give((void **)&(state.dlc_to_warp_to));
  10619.  
  10620.     q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
  10621.     dprint(1, (debugfile,
  10622.         "RESETTING address book... get_nickname_from_addr()!\n"));
  10623.     addrbook_reset();
  10624.     }
  10625.  
  10626.     init_ab_if_needed();
  10627.     save_state(&state);
  10628.  
  10629.     abe = addr_to_abe(adr);
  10630.  
  10631.     if(abe && abe->nickname && abe->nickname[0]){
  10632.     strncpy(buffer, abe->nickname, MAX_NICKNAME);
  10633.     ret = buffer;
  10634.     }
  10635.  
  10636.     restore_state(&state);
  10637.  
  10638.     return(ret);
  10639. }
  10640.  
  10641.  
  10642. /*
  10643.  * Given an address, try to find the first fcc that goes with it.
  10644.  * Copies that fcc into the passed in buffer, which is assumed to
  10645.  * be at least MAXFOLDER+1 in length.  Returns NULL if it can't be found,
  10646.  * else it returns a pointer to the buffer.
  10647.  */
  10648. char *
  10649. get_fcc_from_addr(adr, buffer)
  10650.     ADDRESS *adr;
  10651.     char    *buffer;
  10652. {
  10653.     AdrBk_Entry *abe;
  10654.     char        *ret = NULL;
  10655.     SAVE_STATE_S state;
  10656.     jmp_buf     save_jmp_buf;
  10657.  
  10658.     state.savep = NULL;
  10659.     state.stp = NULL;
  10660.     state.dlc_to_warp_to = NULL;
  10661.  
  10662.     memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
  10663.     if(setjmp(addrbook_changed_unexpectedly)){
  10664.     if(state.savep)
  10665.       fs_give((void **)&(state.savep));
  10666.     if(state.stp)
  10667.       fs_give((void **)&(state.stp));
  10668.     if(state.dlc_to_warp_to)
  10669.       fs_give((void **)&(state.dlc_to_warp_to));
  10670.  
  10671.     q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
  10672.     dprint(1, (debugfile,
  10673.         "RESETTING address book... get_fcc_from_addr()!\n"));
  10674.     addrbook_reset();
  10675.     }
  10676.  
  10677.     init_ab_if_needed();
  10678.     save_state(&state);
  10679.  
  10680.     abe = addr_to_abe(adr);
  10681.  
  10682.     if(abe && abe->fcc && abe->fcc[0]){
  10683.     if(!strcmp(abe->fcc, "\"\""))
  10684.       buffer[0] = '\0';
  10685.     else
  10686.       strncpy(buffer, abe->fcc, MAXFOLDER);
  10687.  
  10688.     ret = buffer;
  10689.     }
  10690.  
  10691.     restore_state(&state);
  10692.  
  10693.     return(ret);
  10694. }
  10695.  
  10696.  
  10697. static char *last_fcc_used;
  10698. /*
  10699.  * Returns alloc'd fcc.
  10700.  */
  10701. char *
  10702. get_fcc(fcc_arg)
  10703.     char *fcc_arg;
  10704. {
  10705.     char *fcc;
  10706.  
  10707.     /*
  10708.      * Use passed in arg unless it is the same as default (and then
  10709.      * may use that anyway below).
  10710.      */
  10711.     if(fcc_arg && strcmp(fcc_arg, ps_global->VAR_DEFAULT_FCC))
  10712.       fcc = cpystr(fcc_arg);
  10713.     else{
  10714.     if(ps_global->fcc_rule == FCC_RULE_LAST && last_fcc_used)
  10715.       fcc = cpystr(last_fcc_used);
  10716.     else if(ps_global->fcc_rule == FCC_RULE_CURRENT
  10717.         && ps_global->mail_stream
  10718.         && !(ps_global->inbox_stream
  10719.             && ps_global->inbox_stream == ps_global->mail_stream)
  10720.         && !IS_NEWS(ps_global->mail_stream)
  10721.         && ps_global->cur_folder
  10722.         && ps_global->cur_folder[0]){
  10723.         CONTEXT_S *cntxt = ps_global->context_current;
  10724.         char *rs = NULL;
  10725.  
  10726.         if(((cntxt->use) & CNTXT_SAVEDFLT))
  10727.           rs = ps_global->cur_folder;
  10728.         else
  10729.           rs = ps_global->mail_stream->mailbox;
  10730.  
  10731.         fcc = cpystr((rs&&*rs) ? rs : ps_global->VAR_DEFAULT_FCC);
  10732.     }
  10733.     else
  10734.       fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
  10735.     }
  10736.     
  10737.     return(fcc);
  10738. }
  10739.  
  10740.  
  10741. /*
  10742.  * Save the fcc for use with next composition.
  10743.  */
  10744. void
  10745. set_last_fcc(fcc)
  10746.     char *fcc;
  10747. {
  10748.     if(fcc){
  10749.     if(!last_fcc_used)
  10750.       last_fcc_used = cpystr(fcc);
  10751.     else if(strcmp(last_fcc_used, fcc)){
  10752.         if(strlen(last_fcc_used) >= strlen(fcc))
  10753.           strcpy(last_fcc_used, fcc);
  10754.         else{
  10755.         fs_give((void **)&last_fcc_used);
  10756.         last_fcc_used = cpystr(fcc);
  10757.         }
  10758.     }
  10759.     }
  10760. }
  10761.  
  10762.  
  10763. /*
  10764.  * Figure out what the fcc is based on the given to address.
  10765.  *
  10766.  * Returns an allocated copy of the fcc, to be freed by the caller, or NULL.
  10767.  */
  10768. char *
  10769. get_fcc_based_on_to(to)
  10770.     ADDRESS *to;
  10771. {
  10772.     ADDRESS    *addr;
  10773.     char       *str, buf[MAX_ADDR_EXPN];
  10774.     BUILDER_ARG tmp_fcc;
  10775.  
  10776.     if(!to || !to->host || to->host[0] == '.')
  10777.       return(NULL);
  10778.  
  10779.     /* pick off first address */
  10780.     addr = copyaddr(to);
  10781.     str = cpystr(addr_string(addr, buf));
  10782.     mail_free_address(&addr);
  10783.     tmp_fcc.tptr = NULL;
  10784.     tmp_fcc.next = NULL;
  10785.     tmp_fcc.xtra = NULL;
  10786.     (void)build_address(str, NULL, NULL, &tmp_fcc);
  10787.  
  10788.     if(str)
  10789.       fs_give((void **)&str);
  10790.     
  10791.     return(tmp_fcc.tptr);
  10792. }
  10793.  
  10794.  
  10795. /*
  10796.  * Look through addrbooks for nickname, opening addrbooks first
  10797.  * if necessary.  It is assumed that the caller will restore the
  10798.  * state of the addrbooks if desired.
  10799.  *
  10800.  * Args: nickname       -- the nickname to lookup
  10801.  *       clearrefs      -- clear reference bits before lookup
  10802.  *       which_addrbook -- If matched, addrbook number it was found in.
  10803.  *       not_here       -- If non-negative, skip looking in this abook.
  10804.  *
  10805.  * Results: A pointer to an AdrBk_Entry is returned, or NULL if not found.
  10806.  * Stop at first match (so order of addrbooks is important).
  10807.  */
  10808. AdrBk_Entry *
  10809. adrbk_lookup_with_opens_by_nick(nickname, clearrefs, which_addrbook, not_here)
  10810.     char *nickname;
  10811.     int   clearrefs;
  10812.     int  *which_addrbook;
  10813.     int   not_here;
  10814. {
  10815.     AdrBk_Entry *abe = (AdrBk_Entry *)NULL;
  10816.     int i;
  10817.     PerAddrBook *pab;
  10818.  
  10819.     dprint(5, (debugfile, "- adrbk_lookup_with_opens_by_nick(%s) -\n",
  10820.     nickname));
  10821.  
  10822.     for(i = 0; i < as.n_addrbk; i++){
  10823.  
  10824.     if(i == not_here)
  10825.       continue;
  10826.  
  10827.     pab = &as.adrbks[i];
  10828.  
  10829.     if(pab->ostatus != Open && pab->ostatus != NoDisplay)
  10830.       init_abook(pab, NoDisplay);
  10831.  
  10832.     if(clearrefs)
  10833.       adrbk_clearrefs(pab->address_book);
  10834.  
  10835.     abe = adrbk_lookup_by_nick(pab->address_book,
  10836.             nickname,
  10837.             (adrbk_cntr_t *)NULL);
  10838.     if(abe)
  10839.       break;
  10840.     }
  10841.  
  10842.     if(abe && which_addrbook)
  10843.       *which_addrbook = i;
  10844.  
  10845.     return(abe);
  10846. }
  10847.  
  10848.  
  10849. /*
  10850.  * Find the addressbook entry that matches the argument address.
  10851.  * Searches through all addressbooks looking for the match.
  10852.  * Opens addressbooks if necessary.  It is assumed that the caller
  10853.  * will restore the state of the addrbooks if desired.
  10854.  *
  10855.  * Args: addr -- the address we're trying to match
  10856.  *
  10857.  * Returns:  NULL -- no match found
  10858.  *           abe  -- a pointer to the addrbook entry that matches
  10859.  */
  10860. AdrBk_Entry *
  10861. addr_to_abe(addr)
  10862.     ADDRESS *addr;
  10863. {
  10864.     register PerAddrBook *pab;
  10865.     int adrbk_number;
  10866.     AdrBk_Entry *abe = NULL;
  10867.     char *abuf = NULL;
  10868.  
  10869.     if(!(addr && addr->mailbox))
  10870.       return (AdrBk_Entry *)NULL;
  10871.  
  10872.     abuf = (char *)fs_get((size_t)(MAX_ADDR_FIELD + 1));
  10873.  
  10874.     strncpy(abuf, addr->mailbox, MAX_ADDR_FIELD);
  10875.     if(addr->host && addr->host[0])
  10876.       strncat(strncat(abuf, "@", MAX_ADDR_FIELD), addr->host, MAX_ADDR_FIELD);
  10877.  
  10878.     /* for each addressbook */
  10879.     for(adrbk_number = 0; adrbk_number < as.n_addrbk; adrbk_number++){
  10880.  
  10881.     pab = &as.adrbks[adrbk_number];
  10882.  
  10883.     if(pab->ostatus != Open && pab->ostatus != NoDisplay)
  10884.       init_abook(pab, NoDisplay);
  10885.  
  10886.     abe = adrbk_lookup_by_addr(pab->address_book,
  10887.                    abuf,
  10888.                    (adrbk_cntr_t *)NULL);
  10889.     if(abe)
  10890.       break;
  10891.     }
  10892.  
  10893.     if(abuf)
  10894.       fs_give((void **)&abuf);
  10895.  
  10896.     return(abe);
  10897. }
  10898.  
  10899.  
  10900. /*
  10901.  * Turn a list of address structures into a formatted string
  10902.  *
  10903.  * Args: adrlist -- An adrlist
  10904.  *       f       -- Function to use to print one address in list.  If NULL,
  10905.  *                  use rfc822_write_address_decode to print whole list.
  10906.  *       verbose -- Include [charset] string if charset doesn't match ours.
  10907.  * Result:  comma separated list of addresses which is
  10908.  *                                     malloced here and returned
  10909.  *        (the list is rfc1522 decoded unless f is *not* NULL)
  10910.  */
  10911. char *
  10912. addr_list_string(adrlist, f, verbose)
  10913.     ADDRESS *adrlist;
  10914.     char    *(*f) PROTO((ADDRESS *, char *));
  10915.     int      verbose;
  10916. {
  10917.     int               len;
  10918.     char             *list, *s, string[MAX_ADDR_EXPN+1];
  10919.     register ADDRESS *a;
  10920.  
  10921.     if(!adrlist)
  10922.       return(cpystr(""));
  10923.     
  10924.     if(f){
  10925.     len = 0;
  10926.     for(a = adrlist; a; a = a->next)
  10927.       len += (strlen((*f)(a, string)) + 2);
  10928.  
  10929.     list = (char *)fs_get((size_t)len);
  10930.     s    = list;
  10931.     s[0] = '\0';
  10932.  
  10933.     for(a = adrlist; a; a = a->next){
  10934.         sstrcpy(&s, (*f)(a, string));
  10935.         if(a->next){
  10936.         *s++ = ',';
  10937.         *s++ = SPACE;
  10938.         }
  10939.     }
  10940.     }
  10941.     else{
  10942.     char *charset = NULL;
  10943.  
  10944.     list = (char *)fs_get((size_t)est_size(adrlist));
  10945.     list[0] = '\0';
  10946.     rfc822_write_address_decode(list, adrlist, verbose ? NULL : &charset);
  10947.     if(charset)
  10948.       fs_give((void **)&charset);
  10949.     }
  10950.  
  10951.     return(list);
  10952. }
  10953.  
  10954.  
  10955. /*
  10956.  * Turn an AdrBk_Entry into a nickname (if it has a nickname) or a
  10957.  * formatted addr_string which has been rfc1522 decoded.
  10958.  *
  10959.  * Args: abe      -- the AdrBk_Entry
  10960.  *       dl       -- the corresponding dl
  10961.  *       abook    -- which addrbook the abe is in (only used for type ListEnt)
  10962.  *
  10963.  * Result:  allocated string is returned
  10964.  */
  10965. char *
  10966. abe_to_nick_or_addr_string(abe, dl, abook)
  10967.     AdrBk_Entry   *abe;
  10968.     AddrScrn_Disp *dl;
  10969.     AdrBk         *abook;
  10970. {
  10971.     ADDRESS       *addr;
  10972.     char          *a_string;
  10973.  
  10974.     if(!dl || !abe)
  10975.       return(cpystr(""));
  10976.  
  10977.     if((dl->type == Simple || dl->type == ListHead)
  10978.        && abe->nickname && abe->nickname[0])
  10979.       return(cpystr(abe->nickname));
  10980.  
  10981.     addr = abe_to_address(abe, dl, abook, NULL);
  10982.     a_string = addr_list_string(addr, NULL, 0); /* always produces a string */
  10983.     if(addr)
  10984.       mail_free_address(&addr);
  10985.     
  10986.     return(a_string);
  10987. }
  10988.  
  10989.  
  10990. /*
  10991.  * Turn an AdrBk_Entry into an address list
  10992.  *
  10993.  * Args: abe      -- the AdrBk_Entry
  10994.  *       dl       -- the corresponding dl
  10995.  *       abook    -- which addrbook the abe is in (only used for type ListEnt)
  10996.  *       how_many -- The number of addresses is returned here
  10997.  *
  10998.  * Result:  allocated address list or NULL
  10999.  */
  11000. ADDRESS *
  11001. abe_to_address(abe, dl, abook, how_many)
  11002.     AdrBk_Entry   *abe;
  11003.     AddrScrn_Disp *dl;
  11004.     AdrBk         *abook;
  11005.     int           *how_many;
  11006. {
  11007.     char          *fullname, *tmp_a_string;
  11008.     char          *list, *l1, **l2;
  11009.     char          *fakedomain = "@";
  11010.     ADDRESS       *addr = NULL;
  11011.     size_t         length;
  11012.     int            count = 0;
  11013.  
  11014.     if(!dl || !abe)
  11015.       return(NULL);
  11016.  
  11017.     fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL;
  11018.  
  11019.     switch(dl->type){
  11020.       case Simple:
  11021.     /* rfc822_parse_adrlist feels free to destroy input so send copy */
  11022.     tmp_a_string = cpystr(abe->addr.addr);
  11023.     rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
  11024.  
  11025.     if(tmp_a_string)
  11026.       fs_give((void **)&tmp_a_string);
  11027.  
  11028.     if(fullname){
  11029.         if(addr->personal)
  11030.           fs_give((void **)&addr->personal);
  11031.  
  11032.         addr->personal = cpystr(adrbk_formatname(fullname));
  11033.     }
  11034.  
  11035.     count++;
  11036.         break;
  11037.  
  11038.       case ListEnt:
  11039.     /* rfc822_parse_adrlist feels free to destroy input so send copy */
  11040.     tmp_a_string = cpystr(listmem_from_dl(abook, dl));
  11041.     rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
  11042.     if(tmp_a_string)
  11043.       fs_give((void **)&tmp_a_string);
  11044.  
  11045.     count++;
  11046.         break;
  11047.  
  11048.       case ListHead:
  11049.     length = 0;
  11050.     for(l2 = abe->addr.list; *l2; l2++)
  11051.       length += (strlen(*l2) + 1);
  11052.  
  11053.     list = (char *)fs_get(length + 1);
  11054.     l1 = list;
  11055.     for(l2 = abe->addr.list; *l2; l2++){
  11056.         if(l1 != list)
  11057.           *l1++ = ',';
  11058.         strcpy(l1, *l2);
  11059.         l1 += strlen(l1);
  11060.         count++;
  11061.     }
  11062.  
  11063.     rfc822_parse_adrlist(&addr, list, fakedomain);
  11064.     if(list)
  11065.       fs_give((void **)&list);
  11066.  
  11067.         break;
  11068.       
  11069.       default:
  11070.     dprint(2,
  11071.        (debugfile, "default case in abe_to_address, shouldn't happen\n"));
  11072.     break;
  11073.     } 
  11074.  
  11075.     if(how_many)
  11076.       *how_many = count;
  11077.  
  11078.     return(addr);
  11079. }
  11080.  
  11081.  
  11082. /* Write RFC822 address with 1522 decoding of personal name
  11083.  * Accepts: pointer to destination string
  11084.  *        address to interpret
  11085.  * (This is a copy of c-client rfc822_write_address except for the decoding.)
  11086.  */
  11087.  
  11088. void
  11089. rfc822_write_address_decode (dest,adr,charset)
  11090.     char    *dest;
  11091.     ADDRESS *adr;
  11092.     char   **charset;
  11093. {
  11094.   extern const char *rspecials;
  11095.  
  11096.   while (adr) {
  11097.                 /* start of group? */
  11098.     if (adr->mailbox && !adr->host) {
  11099.                 /* yes, write group name */
  11100.       rfc822_cat(dest,
  11101.              (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  11102.                         adr->mailbox, charset),
  11103.              rspecials);
  11104.       strcat (dest,": ");    /* write group identifier */
  11105.       adr = adr->next;        /* move to next address block */
  11106.     }
  11107.     else {            /* end of group? */
  11108.       if (!adr->host) strcat (dest,";");
  11109.                 /* simple case? */
  11110.       else if (!(adr->personal || adr->adl)) rfc822_address (dest,adr);
  11111.       else {            /* no, must use phrase <route-addr> form */
  11112.     if (adr->personal) {    /* in case have adl but no personal name */
  11113.       rfc822_cat(dest,
  11114.              (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  11115.                         adr->personal, charset),
  11116.              rspecials);
  11117.       strcat (dest," ");
  11118.     }
  11119.     strcat (dest,"<");    /* write address delimiter */
  11120.     rfc822_address (dest,adr);/* write address */
  11121.     strcat (dest,">");    /* closing delimiter */
  11122.       }
  11123.                 /* delimit if there is one */
  11124.       if ((adr = adr->next) && adr->mailbox) strcat (dest,", ");
  11125.     }
  11126.   }
  11127. }
  11128.  
  11129.  
  11130. /*
  11131.  * Format an address structure into a string
  11132.  *
  11133.  * Args: addr -- Single ADDRESS structure to turn into a string
  11134.  *
  11135.  * Result:  Fills in buf and returns pointer to it.
  11136.  * Just uses the c-client call to do this.
  11137.  *        (the address is not rfc1522 decoded)
  11138.  */
  11139. char *
  11140. addr_string(addr, buf)
  11141.     ADDRESS *addr;
  11142.     char    *buf;
  11143. {
  11144.     ADDRESS tmp_addr;
  11145.  
  11146.     *buf = '\0';
  11147.     tmp_addr = *addr;
  11148.     tmp_addr.next = NULL;
  11149.     rfc822_write_address(buf, &tmp_addr);
  11150.     return(buf);
  11151. }
  11152.  
  11153.  
  11154. /*
  11155.  * Format an address structure into a simple string: "mailbox@host"
  11156.  *
  11157.  * Args: addr -- Single ADDRESS structure to turn into a string
  11158.  *     buf -- buffer to write address in;
  11159.  *
  11160.  * Result:  Returns pointer to internal static formatted string.
  11161.  * Just uses the c-client call to do this.
  11162.  */
  11163. char *
  11164. simple_addr_string(addr, buf)
  11165.     ADDRESS *addr;
  11166.     char    *buf;
  11167. {
  11168.     *buf = '\0';
  11169.     rfc822_address(buf, addr);
  11170.     return(buf);
  11171. }
  11172.  
  11173.  
  11174. /*
  11175.  * Check to see if address is that of the current user running pine
  11176.  *
  11177.  * Args: a  -- Address to check
  11178.  *       ps -- The pine_state structure
  11179.  *
  11180.  * Result: returns 1 if it matches, 0 otherwise.
  11181.  *
  11182.  * The mailbox must match the user name and the hostname must match.
  11183.  * In matching the hostname, matches occur if the hostname in the address
  11184.  * is blank, or if it matches the local hostname, or the full hostname
  11185.  * with qualifying domain, or the qualifying domain without a specific host.
  11186.  * Note, there is a very small chance that we will err on the
  11187.  * non-conservative side here.  That is, we may decide two addresses are
  11188.  * the same even though they are different (because we do case-insensitive
  11189.  * compares on the mailbox).  That might cause a reply not to be sent to
  11190.  * somebody because they look like they are us.  This should be very,
  11191.  * very rare.
  11192.  *
  11193.  * It is also considered a match if any of the addresses in alt-addresses
  11194.  * matches a.  The check there is simpler.  It parses each address in
  11195.  * the list, adding maildomain if there wasn't a domain, and compares
  11196.  * mailbox and host in the ADDRESS's for equality.
  11197.  */
  11198. int
  11199. address_is_us(a, ps)
  11200.     ADDRESS     *a;
  11201.     struct pine *ps;
  11202. {
  11203.     char **t;
  11204.     int ret;
  11205.  
  11206.     if(!a || a->mailbox == NULL)
  11207.       ret = 0;
  11208.  
  11209.     /* at least LHS must match, but case-independent */
  11210.     else if(strucmp(a->mailbox, ps->VAR_USER_ID) == 0
  11211.  
  11212.                       && /* and hostname matches */
  11213.  
  11214.     /* hostname matches if it's not there, */
  11215.     (a->host == NULL ||
  11216.        /* or if hostname and userdomain (the one user sets) match exactly, */
  11217.       ((ps->userdomain && a->host && strucmp(a->host,ps->userdomain) == 0) ||
  11218.  
  11219.               /*
  11220.                * or if(userdomain is either not set or it is set to be
  11221.                * the same as the localdomain or hostname) and (the hostname
  11222.                * of the address matches either localdomain or hostname)
  11223.                */
  11224.              ((ps->userdomain == NULL ||
  11225.                strucmp(ps->userdomain, ps->localdomain) == 0 ||
  11226.                strucmp(ps->userdomain, ps->hostname) == 0) &&
  11227.               (strucmp(a->host, ps->hostname) == 0 ||
  11228.                strucmp(a->host, ps->localdomain) == 0)))))
  11229.       ret = 1;
  11230.  
  11231.     /*
  11232.      * If no match yet, check to see if it matches any of the alternate
  11233.      * addresses the user has specified.
  11234.      */
  11235.     else if(!ps_global->VAR_ALT_ADDRS ||
  11236.        !ps_global->VAR_ALT_ADDRS[0] ||
  11237.        !ps_global->VAR_ALT_ADDRS[0][0])
  11238.     ret = 0;  /* none defined */
  11239.  
  11240.     else{
  11241.     ret = 0;
  11242.     for(t = ps_global->VAR_ALT_ADDRS; *t != NULL; t++){
  11243.         ADDRESS *alt_addr;
  11244.         char    *alt;
  11245.  
  11246.         alt  = cpystr(*t);
  11247.         alt_addr = NULL;
  11248.         rfc822_parse_adrlist(&alt_addr, alt, ps_global->maildomain);
  11249.         if(alt)
  11250.           fs_give((void **)&alt);
  11251.  
  11252.         if(address_is_same(a, alt_addr)){
  11253.         if(alt_addr)
  11254.           mail_free_address(&alt_addr);
  11255.  
  11256.         ret = 1;
  11257.         break;
  11258.         }
  11259.  
  11260.         if(alt_addr)
  11261.           mail_free_address(&alt_addr);
  11262.     }
  11263.     }
  11264.  
  11265.     return(ret);
  11266. }
  11267.  
  11268.  
  11269. /*
  11270.  * Compare the two addresses, and return true if they're the same,
  11271.  * false otherwise
  11272.  *
  11273.  * Args: a -- First address for comparison
  11274.  *       b -- Second address for comparison
  11275.  *
  11276.  * Result: returns 1 if it matches, 0 otherwise.
  11277.  */
  11278. int
  11279. address_is_same(a, b)
  11280.     ADDRESS *a, *b;
  11281. {
  11282.     return(a && b && a->mailbox && b->mailbox && a->host && b->host
  11283.        && strucmp(a->mailbox, b->mailbox) == 0
  11284.            && strucmp(a->host, b->host) == 0);
  11285. }
  11286.  
  11287.  
  11288. /*
  11289.  * Compute an upper bound on the size of the array required by
  11290.  * rfc822_write_address for this list of addresses.
  11291.  *
  11292.  * Args: adrlist -- The address list.
  11293.  *
  11294.  * Returns -- an integer giving the upper bound
  11295.  */
  11296. int
  11297. est_size(adrlist)
  11298.     ADDRESS *adrlist;
  11299. {
  11300.     ADDRESS      *b;
  11301.     register int   cnt = 0;
  11302.     register char *p;
  11303.  
  11304.     for(b = adrlist; b; b = b->next){
  11305.     if(b->personal){
  11306.         extern char *tspecials;        /* from c-client rfc822.c */
  11307.  
  11308.         cnt += strlen(b->personal);
  11309.         /* Look for any chars special to RFC822 whose quoting
  11310.          * will make the address longer when turned into a string.
  11311.          */
  11312.         if(strpbrk(b->personal, tspecials)){
  11313.         cnt += 2;
  11314.         for(p=strpbrk(b->personal,"\\\""); p; p=strpbrk(++p,"\\\""))
  11315.           cnt += 2;
  11316.         }
  11317.     }
  11318.  
  11319.     cnt   += (b->mailbox  ? strlen(b->mailbox)  : 0);
  11320.     cnt   += (b->adl      ? strlen(b->adl)      : 0);
  11321.     cnt   += (b->host     ? strlen(b->host)     : 0);
  11322.     /*
  11323.      * add room for:
  11324.          *   possible single space between fullname and addr
  11325.          *   left and right brackets
  11326.          *   @ sign
  11327.          *   possible : for route addr
  11328.          *   , <space>
  11329.      *
  11330.      * So I really think that adding 7 is enough.  Instead, I'll add 10.
  11331.      */
  11332.     cnt   += 10;
  11333.     }
  11334.  
  11335.     return(max(cnt, 200));  /* just making sure */
  11336. }
  11337.  
  11338.  
  11339. /*
  11340.  * Interact with user to figure out which address book they want to add a
  11341.  * new entry (TakeAddr) to.
  11342.  *
  11343.  * Args: command_line -- just the line to prompt on
  11344.  *
  11345.  * Results: returns a pab pointing to the selected addrbook, or NULL.
  11346.  */
  11347. PerAddrBook *
  11348. use_this_addrbook(command_line)
  11349.     int command_line;
  11350. {
  11351.     HelpType   help;
  11352.     int        rc = 0;
  11353.     PerAddrBook  *pab, *the_only_pab;
  11354. #define MAX_ABOOK 1000
  11355.     int        i, abook_num, count_read_write;
  11356.     char       addrbook[MAX_ABOOK + 1],
  11357.                prompt[MAX_ABOOK + 81];
  11358.     static ESCKEY_S ekey[] = {
  11359.     {-2,   0,   NULL, NULL},
  11360.     {ctrl('P'), 10, "^P", "Prev AddrBook"},
  11361.     {ctrl('N'), 11, "^N", "Next AddrBook"},
  11362.     {KEY_UP,    10, "", ""},
  11363.     {KEY_DOWN,  11, "", ""},
  11364.     {-1, 0, NULL, NULL}};
  11365.  
  11366.     dprint(8, (debugfile, "\n - use_this_addrbook -\n"));
  11367.  
  11368.     /* check for only one ReadWrite addrbook */
  11369.     count_read_write = 0;
  11370.     for(i = 0; i < as.n_addrbk; i++){
  11371.     pab = &as.adrbks[i];
  11372.     /*
  11373.      * NoExists is counted, too, so the user can add to an empty
  11374.      * addrbook the first time.
  11375.      */
  11376.     if(pab->access == ReadWrite || pab->access == NoExists){
  11377.         count_read_write++;
  11378.         the_only_pab = &as.adrbks[i];
  11379.     }
  11380.     }
  11381.  
  11382.     /* only one usable addrbook, use it */
  11383.     if(count_read_write == 1)
  11384.       return(the_only_pab);
  11385.  
  11386.     /* no addrbook to write to */
  11387.     if(count_read_write == 0){
  11388.     q_status_message1(SM_ORDER | SM_DING, 3, 4,
  11389.               "No %sAddressbook to Take to!",
  11390.               (as.n_addrbk > 0) ? "writable " : "");
  11391.     return NULL;
  11392.     }
  11393.  
  11394.     /* start with the first addrbook */
  11395.     abook_num = 0;
  11396.     pab       = &as.adrbks[abook_num];
  11397.     strncpy(addrbook, pab->nickname, MAX_ABOOK);
  11398.     addrbook[MAX_ABOOK] = '\0';
  11399.     sprintf(prompt, "Take to which addrbook : %s",
  11400.     (pab->access == ReadOnly || pab->access == NoAccess) ?
  11401.         "[ReadOnly] " : "");
  11402.     help = NO_HELP;
  11403.     ps_global->mangled_footer = 1;
  11404.     do{
  11405.     if(!pab)
  11406.           q_status_message1(SM_ORDER, 3, 4, "No addressbook \"%s\"", addrbook);
  11407.  
  11408.     if(rc == 3)
  11409.           help = (help == NO_HELP ? h_oe_chooseabook : NO_HELP);
  11410.  
  11411.     rc = optionally_enter(addrbook, command_line, 0, MAX_ABOOK, 1,
  11412.                                   0, prompt, ekey, help, 0);
  11413.  
  11414.     if(rc == 1) /* ^C */
  11415.       break;
  11416.  
  11417.     if(rc == 10){            /* Previous addrbook */
  11418.         if(--abook_num < 0)
  11419.           abook_num = as.n_addrbk - 1;
  11420.  
  11421.         pab = &as.adrbks[abook_num];
  11422.         strncpy(addrbook, pab->nickname, MAX_ABOOK);
  11423.         sprintf(prompt, "Take to which addrbook : %s",
  11424.         (pab->access == ReadOnly || pab->access == NoAccess) ?
  11425.             "[ReadOnly] " : "");
  11426.     }
  11427.     else if(rc == 11){            /* Next addrbook */
  11428.         if(++abook_num > as.n_addrbk - 1)
  11429.           abook_num = 0;
  11430.  
  11431.         pab = &as.adrbks[abook_num];
  11432.         strncpy(addrbook, pab->nickname, MAX_ABOOK);
  11433.         sprintf(prompt, "Take to which addrbook : %s",
  11434.         (pab->access == ReadOnly || pab->access == NoAccess) ?
  11435.             "[ReadOnly] " : "");
  11436.     }
  11437.  
  11438.     }while(rc == 2 || rc == 3 || rc == 4 || rc == 10 || rc == 11 || rc == 12 ||
  11439.            !(pab = check_for_addrbook(addrbook)));
  11440.             
  11441.     if(rc != 0)
  11442.       return NULL;
  11443.  
  11444.     return(pab);
  11445. }
  11446.  
  11447.  
  11448. /*
  11449.  * Return a pab pointer to the addrbook which corresponds to the argument.
  11450.  * 
  11451.  * Args: addrbook -- the string representing the addrbook.
  11452.  *
  11453.  * Results: returns a PerAddrBook pointer for the referenced addrbook, NULL
  11454.  *          if none.  First the nicknames are checked and then the filenames.
  11455.  *          This must be one of the existing addrbooks.
  11456.  */
  11457. PerAddrBook *
  11458. check_for_addrbook(addrbook)
  11459.     char *addrbook;
  11460. {
  11461.     register int i;
  11462.     register PerAddrBook *pab;
  11463.  
  11464.     for(i = 0; i < as.n_addrbk; i++){
  11465.     pab = &as.adrbks[i];
  11466.     if(strucmp(pab->nickname, addrbook) == 0)
  11467.       break;
  11468.     }
  11469.  
  11470.     if(i < as.n_addrbk)
  11471.       return(pab);
  11472.  
  11473.     for(i = 0; i < as.n_addrbk; i++){
  11474.     pab = &as.adrbks[i];
  11475.     if(strucmp(pab->filename, addrbook) == 0)
  11476.       break;
  11477.     }
  11478.  
  11479.     if(i < as.n_addrbk)
  11480.       return(pab);
  11481.     
  11482.     return NULL;
  11483. }
  11484.  
  11485.  
  11486. static struct key takeaddr_keys_listmode[] = 
  11487.        {{"?","Help",KS_SCREENHELP},    {"W","WhereIs",KS_WHEREIS},
  11488.     {"E","ExitTake",KS_EXITMODE},    {"T","Take",KS_NONE},
  11489.     {"P","Prev",KS_NONE},        {"N","Next", KS_NONE},
  11490.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  11491.     {"X","[Set/Unset]",KS_NONE},    {"A","SetAll",KS_NONE},
  11492.     {"U","UnSetAll",KS_NONE},    {"S","SinglMode",KS_NONE}};
  11493. INST_KEY_MENU(takeaddr_keymenu_listmode, takeaddr_keys_listmode);
  11494.  
  11495. static struct key takeaddr_keys_singlemode[] = 
  11496.        {{"?","Help",KS_SCREENHELP},    {"W","WhereIs",KS_WHEREIS},
  11497.     {"E","ExitTake",KS_EXITMODE},    {"T","[Take]",KS_NONE},
  11498.     {"P","Prev",KS_NONE},        {"N","Next", KS_NONE},
  11499.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  11500.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  11501.     {NULL,NULL,KS_NONE},        {"L","ListMode",KS_NONE}};
  11502. INST_KEY_MENU(takeaddr_keymenu_singlemode, takeaddr_keys_singlemode);
  11503.  
  11504.  
  11505. /*
  11506.  * Screen for selecting which addresses to Take to address book.
  11507.  *
  11508.  * Args:      ps -- Pine state
  11509.  *       ta_list -- Screen is formed from this list of addresses
  11510.  *  how_many_selected -- how many checked initially in ListMode
  11511.  *          mode -- which mode to start in
  11512.  *           fcc -- use this fcc if no other available
  11513.  *       comment -- use this comment if no other available
  11514.  *
  11515.  * Result: an address book may be updated
  11516.  */
  11517. void
  11518. takeaddr_screen(ps, ta_list, how_many_selected, mode)
  11519.     struct pine       *ps;
  11520.     TA_S              *ta_list;
  11521.     int                how_many_selected;
  11522.     TakeAddrScreenMode mode;
  11523. {
  11524.     int          orig_ch, dline, give_warn_message, command_line;
  11525.     int           ch = 'x',
  11526.                   km_popped = 0,
  11527.                   directly_to_take = 0,
  11528.                   done = 0;
  11529.     TA_S     *current = NULL,
  11530.          *ctmp = NULL;
  11531.     TA_SCREEN_S   screen;
  11532.     Pos           cursor_pos;
  11533.  
  11534.     command_line = -FOOTER_ROWS(ps);  /* third line from the bottom */
  11535.  
  11536.     screen.current = screen.top_line = NULL;
  11537.     screen.mode    = mode;
  11538.  
  11539.     if(ta_list == NULL){
  11540.     q_status_message(SM_INFO, 0, 2, "No addresses to take, cancelled");
  11541.     return;
  11542.     }
  11543.  
  11544.     current           = first_sel_taline(ta_list);
  11545.     ps->mangled_screen = 1;
  11546.     ta_screen           = &screen;
  11547.  
  11548.     if(is_talist_of_one(current)){
  11549.     directly_to_take++;
  11550.     screen.mode = SingleMode; 
  11551.     }
  11552.     else if(screen.mode == ListMode)
  11553.       q_status_message(SM_INFO, 0, 1,
  11554.         "List mode: Use \"X\" to mark addresses to be included in list");
  11555.     else
  11556.       q_status_message(SM_INFO, 0, 1,
  11557.         "Single mode: Use \"P\" or \"N\" to select desired address");
  11558.  
  11559.     while(!done){
  11560.     if(km_popped){
  11561.         km_popped--;
  11562.         if(km_popped == 0){
  11563.         clearfooter(ps);
  11564.         ps->mangled_body = 1;
  11565.         }
  11566.     }
  11567.  
  11568.     if(screen.mode == ListMode)
  11569.       ps->redrawer = takeaddr_screen_redrawer_list;
  11570.     else
  11571.       ps->redrawer = takeaddr_screen_redrawer_single;
  11572.  
  11573.     if(ps->mangled_screen){
  11574.         ps->mangled_header = 1;
  11575.         ps->mangled_footer = 1;
  11576.         ps->mangled_body   = 1;
  11577.         ps->mangled_screen = 0;
  11578.     }
  11579.  
  11580.     /*----------- Check for new mail -----------*/
  11581.     if(new_mail(0, NM_TIMING(ch), 1) >= 0)
  11582.       ps->mangled_header = 1;
  11583.  
  11584. #ifdef _WINDOWS
  11585.     mswin_beginupdate();
  11586. #endif
  11587.     if(ps->mangled_header){
  11588.         char tbuf[40];
  11589.  
  11590.         sprintf(tbuf, "TAKE ADDRESS SCREEN (%s Mode)",
  11591.                     (screen.mode == ListMode) ? "List"
  11592.                                   : "Single");
  11593.             set_titlebar(tbuf, ps->mail_stream, ps->context_current,
  11594.                              ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0);
  11595.         ps->mangled_header = 0;
  11596.     }
  11597.  
  11598.     dline = update_takeaddr_screen(ps, current, &screen, &cursor_pos);
  11599.     if(F_OFF(F_SHOW_CURSOR, ps)){
  11600.         cursor_pos.row = ps->ttyo->screen_rows - FOOTER_ROWS(ps);
  11601.         cursor_pos.col = 0;
  11602.     }
  11603.  
  11604.     /*---- This displays new mail notification, or errors ---*/
  11605.     if(km_popped){
  11606.         FOOTER_ROWS(ps_global) = 3;
  11607.         mark_status_unknown();
  11608.     }
  11609.  
  11610.         display_message(ch);
  11611.     if(km_popped){
  11612.         FOOTER_ROWS(ps_global) = 1;
  11613.         mark_status_unknown();
  11614.     }
  11615.  
  11616.     /*---- Redraw footer ----*/
  11617.     if(ps->mangled_footer){
  11618.         bitmap_t bitmap;
  11619.  
  11620.         if(km_popped){
  11621.         FOOTER_ROWS(ps) = 3;
  11622.         clearfooter(ps);
  11623.         }
  11624.  
  11625.         setbitmap(bitmap);
  11626.         ps->mangled_footer = 0;
  11627.         if(screen.mode == ListMode)
  11628.           draw_keymenu(&takeaddr_keymenu_listmode, bitmap,
  11629.             ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps_global),
  11630.             0, FirstMenu, 0);
  11631.         else
  11632.           draw_keymenu(&takeaddr_keymenu_singlemode, bitmap,
  11633.             ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps_global),
  11634.             0, FirstMenu, 0);
  11635.         if(km_popped){
  11636.         FOOTER_ROWS(ps) = 1;
  11637.         mark_keymenu_dirty();
  11638.         }
  11639.     }
  11640.  
  11641. #ifdef _WINDOWS
  11642.     mswin_endupdate();
  11643. #endif
  11644.         /*------ Read the command from the keyboard ----*/
  11645.     MoveCursor(cursor_pos.row, cursor_pos.col);
  11646.  
  11647. #ifdef    MOUSE
  11648.     mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
  11649.     register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
  11650.                ps_global->ttyo->screen_rows - (FOOTER_ROWS(ps) + 1),
  11651.                ps_global->ttyo->screen_cols);
  11652. #endif
  11653.  
  11654.     if(directly_to_take)  /* bypass this screen */
  11655.       ch = orig_ch = 't';
  11656.     else
  11657.       ch = orig_ch = read_command();
  11658.  
  11659. #ifdef    MOUSE
  11660.     clear_mfunc(mouse_in_content);
  11661. #endif
  11662.  
  11663.         if(ch <= 0xff && isupper((unsigned char)ch))
  11664.           ch = tolower((unsigned char)ch);
  11665.  
  11666.     if(km_popped)
  11667.       switch(ch){
  11668.         case NO_OP_IDLE:
  11669.         case NO_OP_COMMAND: 
  11670.         case KEY_RESIZE:
  11671.         case ctrl('L'):
  11672.           km_popped++;
  11673.           break;
  11674.         
  11675.         default:
  11676.           clearfooter(ps);
  11677.           break;
  11678.       }
  11679.  
  11680.     switch(ch){
  11681.       case '?':                /* help! */
  11682.       case PF1:
  11683.       case ctrl('G'):
  11684.         if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
  11685.         km_popped = 2;
  11686.         ps_global->mangled_footer = 1;
  11687.         break;
  11688.         }
  11689.  
  11690.         helper(h_takeaddr_screen, "HELP FOR TAKE ADDRESS SCREEN", 1);
  11691.         ps->mangled_screen = 1;
  11692.         break;
  11693.  
  11694.       case 'e':                /* exit takeaddr screen */
  11695.       case PF3:
  11696.       case ctrl('C'):
  11697.         cancel_warning(NO_DING, "addition");
  11698.         done++;
  11699.         break;
  11700.  
  11701.       case 't':  /* take */
  11702.       case PF4:
  11703.       case ctrl('M'):
  11704.       case ctrl('J'):
  11705.       if((ch == ctrl('M') || ch == ctrl('J'))
  11706.          && screen.mode == ListMode)
  11707.         goto SelectCase;  /* default is different in this case */
  11708.  
  11709.       TakeAddrCase:
  11710.         if(screen.mode == ListMode)
  11711.           done = ta_take_marked_addrs(how_many_selected,
  11712.                first_sel_taline(current), command_line);
  11713.         else
  11714.           done = ta_take_single_addr(current, command_line);
  11715.  
  11716.         if(!done)
  11717.           directly_to_take = 0;
  11718.  
  11719.         break;
  11720.  
  11721.       case 'n':                /* next list element */
  11722.       case PF6:
  11723.       case '\t':
  11724.       case ctrl('N'):
  11725.       case KEY_DOWN:
  11726.       case KEY_SCRLDNL:
  11727.         if(ctmp = next_sel_taline(current))
  11728.           current = ctmp;
  11729.         else
  11730.           q_status_message(SM_INFO, 0, 1, "Already on last line.");
  11731.  
  11732.         break;
  11733.  
  11734.       case 'p':                /* previous list element */
  11735.       case PF5:
  11736.       case ctrl('P'):            /* up arrow */
  11737.       case KEY_UP:
  11738.       case KEY_SCRLUPL:
  11739.         if(ctmp = pre_sel_taline(current))
  11740.           current = ctmp;
  11741.         else
  11742.           q_status_message(SM_INFO, 0, 1, "Already on first line.");
  11743.  
  11744.         break;
  11745.  
  11746.       case '+':                /* page forward */
  11747.       case SPACE:
  11748.       case PF8:
  11749.       case ctrl('V'):
  11750.       case KEY_PGDN:
  11751.         give_warn_message = 1;
  11752.         while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){
  11753.             if(ctmp = next_sel_taline(current)){
  11754.             current = ctmp;
  11755.             give_warn_message = 0;
  11756.         }
  11757.             else
  11758.             break;
  11759.         }
  11760.  
  11761.         if(give_warn_message)
  11762.           q_status_message(SM_INFO, 0, 1, "Already on last page.");
  11763.  
  11764.         break;
  11765.  
  11766.       case '-':                /* page backward */
  11767.       case PF7:
  11768.       case ctrl('Y'):
  11769.       case KEY_PGUP:
  11770.         /* move to top of screen */
  11771.         give_warn_message = 1;
  11772.         while(dline-- > HEADER_ROWS(ps_global)){
  11773.             if(ctmp = pre_sel_taline(current)){
  11774.             current = ctmp;
  11775.             give_warn_message = 0;
  11776.         }
  11777.             else
  11778.             break;
  11779.         }
  11780.  
  11781.         /* page back one screenful */
  11782.         while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){
  11783.             if(ctmp = pre_sel_taline(current)){
  11784.             current = ctmp;
  11785.             give_warn_message = 0;
  11786.         }
  11787.             else
  11788.             break;
  11789.         }
  11790.  
  11791.         if(give_warn_message)
  11792.           q_status_message(SM_INFO, 0, 1, "Already on first page.");
  11793.  
  11794.         break;
  11795.  
  11796.       case 'w':                /* whereis */
  11797.       case PF2:
  11798.       case ctrl('W'):
  11799.         if(ctmp = whereis_taline(current))
  11800.           current = ctmp;
  11801.         
  11802.         ps->mangled_footer = 1;
  11803.         break;
  11804.  
  11805.       case KEY_SCRLTO:
  11806.         /* no op for now */
  11807.         break;
  11808.  
  11809. #ifdef MOUSE        
  11810.       case KEY_MOUSE:
  11811.         {
  11812.         MOUSEPRESS mp;
  11813.  
  11814.         mouse_get_last(NULL, &mp);
  11815.         mp.row -= HEADER_ROWS(ps_global);
  11816.         ctmp = screen.top_line;
  11817.         if(mp.doubleclick){
  11818.             if(screen.mode == SingleMode) 
  11819.               goto TakeAddrCase;
  11820.             else
  11821.               goto SelectCase;
  11822.         }
  11823.         else{
  11824.             while(mp.row && ctmp != NULL){
  11825.             --mp.row;
  11826.             do  ctmp = ctmp->next;
  11827.             while(ctmp != NULL && ctmp->skip_it && !ctmp->print);
  11828.             }
  11829.  
  11830.             if(ctmp != NULL && !ctmp->skip_it)
  11831.               current = ctmp;
  11832.         }
  11833.         }
  11834.         break;
  11835. #endif
  11836.  
  11837.       case ctrl('L'):
  11838.           case KEY_RESIZE:
  11839.         ClearScreen();
  11840.         ps->mangled_screen = 1;
  11841.         break;
  11842.  
  11843.       case 'x':  /* select or unselect this addr */
  11844.       case PF9:
  11845.       SelectCase:
  11846.         if(screen.mode != ListMode)
  11847.           goto bleep;
  11848.  
  11849.         current->checked = 1 - current->checked;  /* flip it */
  11850.         how_many_selected += (current->checked ? 1 : -1);
  11851.         break;
  11852.  
  11853.       case 'a':  /* select all */
  11854.       case PF10:
  11855.         if(screen.mode != ListMode)
  11856.           goto bleep;
  11857.  
  11858.         how_many_selected = ta_mark_all(first_sel_taline(current));
  11859.         ps->mangled_body = 1;
  11860.         break;
  11861.  
  11862.       case 'u':  /* unselect all */
  11863.       case PF11:
  11864.         if(screen.mode != ListMode)
  11865.           goto bleep;
  11866.  
  11867.         how_many_selected = ta_unmark_all(first_sel_taline(current));
  11868.         ps->mangled_body = 1;
  11869.         break;
  11870.  
  11871.       case 's':  /* switch to SingleMode */
  11872.       case 'l':  /* switch to ListMode */
  11873.       case PF12:
  11874.         if(screen.mode == ListMode && ch == 'l'){
  11875.         q_status_message(SM_INFO, 0, 1,
  11876.            "Already in ListMode.  Press \"S\" for Single entry mode.");
  11877.         break;
  11878.         }
  11879.  
  11880.         if(screen.mode == SingleMode && ch == 's'){
  11881.         q_status_message(SM_INFO, 0, 1,
  11882.            "Already in SingleMode.  Press \"L\" for List entry mode.");
  11883.         break;
  11884.         }
  11885.  
  11886.         if(screen.mode == ListMode){
  11887.         screen.mode = SingleMode;
  11888.         q_status_message(SM_INFO, 0, 1,
  11889.           "Single mode: Use \"P\" or \"N\" to select desired address");
  11890.         }
  11891.         else{
  11892.         screen.mode = ListMode;
  11893.         q_status_message(SM_INFO, 0, 1,
  11894.         "List mode: Use \"X\" to mark addresses to be included in list");
  11895.  
  11896.         if(how_many_selected <= 1){
  11897.             how_many_selected =
  11898.                     ta_unmark_all(first_sel_taline(current));
  11899.             current->checked = 1;
  11900.             how_many_selected++;
  11901.         }
  11902.         }
  11903.  
  11904.         ps->mangled_screen = 1;
  11905.         break;
  11906.  
  11907.       default:
  11908.       bleep:
  11909.         bogus_command(orig_ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
  11910.  
  11911.       case NO_OP_IDLE:            /* simple timeout */
  11912.       case NO_OP_COMMAND:
  11913.         break;
  11914.     }
  11915.     }
  11916.  
  11917.     /* clean up */
  11918.     if(current)
  11919.       while(current->prev)
  11920.     current = current->prev;
  11921.  
  11922.     while(current){
  11923.     ctmp = current->next;
  11924.     free_taline(¤t);
  11925.     current = ctmp;
  11926.     }
  11927.  
  11928.     ps->mangled_screen = 1;
  11929. }
  11930.  
  11931.  
  11932. /*
  11933.  * Previous selectable TA line.
  11934.  * skips over the elements with skip_it set
  11935.  */
  11936. TA_S *
  11937. pre_sel_taline(current)
  11938.     TA_S *current;
  11939. {
  11940.     TA_S *p;
  11941.  
  11942.     if(!current)
  11943.       return NULL;
  11944.  
  11945.     p = current->prev;
  11946.     while(p && p->skip_it)
  11947.       p = p->prev;
  11948.     
  11949.     return(p);
  11950. }
  11951.  
  11952.  
  11953. /*
  11954.  * Previous TA line, selectable or just printable.
  11955.  * skips over the elements with skip_it set
  11956.  */
  11957. TA_S *
  11958. pre_taline(current)
  11959.     TA_S *current;
  11960. {
  11961.     TA_S *p;
  11962.  
  11963.     if(!current)
  11964.       return NULL;
  11965.  
  11966.     p = current->prev;
  11967.     while(p && (p->skip_it && !p->print))
  11968.       p = p->prev;
  11969.     
  11970.     return(p);
  11971. }
  11972.  
  11973.  
  11974. /*
  11975.  * Next selectable TA line.
  11976.  * skips over the elements with skip_it set
  11977.  */
  11978. TA_S *
  11979. next_sel_taline(current)
  11980.     TA_S *current;
  11981. {
  11982.     TA_S *p;
  11983.  
  11984.     if(!current)
  11985.       return NULL;
  11986.  
  11987.     p = current->next;
  11988.     while(p && p->skip_it)
  11989.       p = p->next;
  11990.     
  11991.     return(p);
  11992. }
  11993.  
  11994.  
  11995. /*
  11996.  * Next TA line, including print only lines.
  11997.  * skips over the elements with skip_it set unless they are print lines
  11998.  */
  11999. TA_S *
  12000. next_taline(current)
  12001.     TA_S *current;
  12002. {
  12003.     TA_S *p;
  12004.  
  12005.     if(!current)
  12006.       return NULL;
  12007.  
  12008.     p = current->next;
  12009.     while(p && (p->skip_it && !p->print))
  12010.       p = p->next;
  12011.     
  12012.     return(p);
  12013. }
  12014.  
  12015.  
  12016. /*
  12017.  * WhereIs for TakeAddr screen.
  12018.  *
  12019.  * Returns the line match is found in or NULL.
  12020.  */
  12021. TA_S *
  12022. whereis_taline(current)
  12023.     TA_S *current;
  12024. {
  12025.     TA_S *p;
  12026.     int   rc, found = 0, wrapped = 0;
  12027.     char *result = NULL, buf[MAX_SEARCH+1], tmp[MAX_SEARCH+3];
  12028.     static char last[MAX_SEARCH+1];
  12029.     HelpType help;
  12030.     static ESCKEY_S ekey[] = {
  12031.     {0, 0, "", ""},
  12032.     {ctrl('Y'), 10, "^Y", "Top"},
  12033.     {ctrl('V'), 11, "^V", "Bottom"},
  12034.     {-1, 0, NULL, NULL}};
  12035.  
  12036.     if(!current)
  12037.       return NULL;
  12038.  
  12039.     /*--- get string  ---*/
  12040.     buf[0] = '\0';
  12041.     sprintf(tmp, "Word to find %s%s%s: ",
  12042.          (last[0]) ? "[" : "",
  12043.          (last[0]) ? last : "",
  12044.          (last[0]) ? "]" : "");
  12045.     help = NO_HELP;
  12046.     while(1){
  12047.     rc = optionally_enter(buf,-FOOTER_ROWS(ps_global),0,MAX_SEARCH,1,0,
  12048.                  tmp,ekey,help,0);
  12049.     if(rc == 3)
  12050.       help = help == NO_HELP ? h_config_whereis : NO_HELP;
  12051.     else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){
  12052.         if(rc == 0 && !buf[0] && last[0])
  12053.           strcpy(buf, last);
  12054.  
  12055.         break;
  12056.     }
  12057.     }
  12058.  
  12059.     if(rc == 0 && buf[0]){
  12060.     p = current;
  12061.     while(p = next_taline(p))
  12062.       if(srchstr((char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  12063.                         p->strvalue, NULL),
  12064.              buf)){
  12065.           found++;
  12066.           break;
  12067.       }
  12068.  
  12069.     if(!found){
  12070.         p = first_taline(current);
  12071.  
  12072.         while(p != current)
  12073.           if(srchstr((char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  12074.                         p->strvalue, NULL),
  12075.              buf)){
  12076.           found++;
  12077.           wrapped++;
  12078.           break;
  12079.           }
  12080.           else
  12081.         p = next_taline(p);
  12082.     }
  12083.     }
  12084.     else if(rc == 10){
  12085.     current = first_sel_taline(current);
  12086.     result = "Searched to top";
  12087.     }
  12088.     else if(rc == 11){
  12089.     current = last_sel_taline(current);
  12090.     result = "Searched to bottom";
  12091.     }
  12092.     else{
  12093.     current = NULL;
  12094.     result = "WhereIs cancelled";
  12095.     }
  12096.  
  12097.     if(found){
  12098.     current = p;
  12099.     result  = wrapped ? "Search wrapped to beginning" : "Word found";
  12100.     strcpy(last, buf);
  12101.     }
  12102.  
  12103.     q_status_message(SM_ORDER,0,3,result ? result : "Word not found");
  12104.     return(current);
  12105. }
  12106.  
  12107.  
  12108. /*
  12109.  * Call the addrbook functions which add the checked addresses.
  12110.  *
  12111.  * Args: how_many_selected -- how many addresses are checked
  12112.  *                  f_line -- the first ta line
  12113.  *
  12114.  * Returns: 1 -- we're done, caller should return
  12115.  *          0 -- we're not done
  12116.  */
  12117. int
  12118. ta_take_marked_addrs(how_many_selected, f_line, command_line)
  12119.     int   how_many_selected;
  12120.     TA_S *f_line;
  12121.     int   command_line;
  12122. {
  12123.     char **new_list;
  12124.     TA_S  *p;
  12125.  
  12126.     if(how_many_selected == 0){
  12127.     q_status_message(SM_ORDER, 0, 4,
  12128.   "No addresses marked for taking. Use ExitTake to leave TakeAddr screen");
  12129.     return 0;
  12130.     }
  12131.  
  12132.     if(how_many_selected == 1){
  12133.     for(p = f_line; p; p = next_sel_taline(p))
  12134.       if(p->checked && !p->skip_it)
  12135.         break;
  12136.  
  12137.     if(p)
  12138.       add_abook_entry(p,
  12139.               (p->nickname && p->nickname[0]) ? p->nickname : NULL,
  12140.               (p->fullname && p->fullname[0]) ? p->fullname : NULL,
  12141.               (p->fcc && p->fcc[0]) ? p->fcc : NULL,
  12142.               (p->comment && p->comment[0]) ? p->comment : NULL,
  12143.               command_line);
  12144.     }
  12145.     else{
  12146.     new_list = list_of_checked(f_line);
  12147.     for(p = f_line; p; p = next_sel_taline(p))
  12148.       if(p->checked && !p->skip_it)
  12149.         break;
  12150.  
  12151.     take_to_addrbooks_frontend(new_list, p ? p->nickname : NULL,
  12152.         p ? p->fullname : NULL, NULL, p ? p->fcc : NULL,
  12153.         p ? p->comment : NULL, command_line);
  12154.     free_list(&new_list);
  12155.     }
  12156.  
  12157.     return 1;
  12158. }
  12159.  
  12160.  
  12161. int
  12162. ta_take_single_addr(cur, command_line)
  12163.     TA_S *cur;
  12164.     int   command_line;
  12165. {
  12166.     add_abook_entry(cur,
  12167.             (cur->nickname && cur->nickname[0]) ? cur->nickname : NULL,
  12168.             (cur->fullname && cur->fullname[0]) ? cur->fullname : NULL,
  12169.             (cur->fcc && cur->fcc[0]) ? cur->fcc : NULL,
  12170.             (cur->comment && cur->comment[0]) ? cur->comment : NULL,
  12171.             command_line);
  12172.  
  12173.     return 1;
  12174. }
  12175.  
  12176.  
  12177. /*
  12178.  * Mark all of the addresses with a check.
  12179.  *
  12180.  * Args: f_line -- the first ta line
  12181.  *
  12182.  * Returns the number of lines checked.
  12183.  */
  12184. int
  12185. ta_mark_all(f_line)
  12186.     TA_S *f_line;
  12187. {
  12188.     TA_S *ctmp;
  12189.     int   how_many_selected = 0;
  12190.  
  12191.     for(ctmp = f_line; ctmp; ctmp = next_sel_taline(ctmp)){
  12192.     ctmp->checked = 1;
  12193.     how_many_selected++;
  12194.     }
  12195.  
  12196.     return(how_many_selected);
  12197. }
  12198.  
  12199.  
  12200. /*
  12201.  * Does the takeaddr list consist of a single address?
  12202.  *
  12203.  * Args: f_line -- the first ta line
  12204.  *
  12205.  * Returns 1 if only one address and 0 otherwise.
  12206.  */
  12207. int
  12208. is_talist_of_one(f_line)
  12209.     TA_S *f_line;
  12210. {
  12211.     if(f_line == NULL)
  12212.       return 0;
  12213.  
  12214.     /* there is at least one, see if there are two */
  12215.     if(next_sel_taline(f_line) != NULL)
  12216.       return 0;
  12217.     
  12218.     return 1;
  12219. }
  12220.  
  12221.  
  12222. /*
  12223.  * Turn off all of the check marks.
  12224.  *
  12225.  * Args: f_line -- the first ta line
  12226.  *
  12227.  * Returns the number of lines checked (0).
  12228.  */
  12229. int
  12230. ta_unmark_all(f_line)
  12231.     TA_S *f_line;
  12232. {
  12233.     TA_S *ctmp;
  12234.  
  12235.     for(ctmp = f_line; ctmp; ctmp = ctmp->next)
  12236.       ctmp->checked = 0;
  12237.  
  12238.     return 0;
  12239. }
  12240.  
  12241.  
  12242. /*
  12243.  * Manage display of the Take Address screen.
  12244.  *
  12245.  * Args:     ps -- pine state
  12246.  *      current -- the current TA line
  12247.  *       screen -- the TA screen
  12248.  *   cursor_pos -- return good cursor position here
  12249.  */
  12250. int
  12251. update_takeaddr_screen(ps, current, screen, cursor_pos)
  12252.     struct pine  *ps;
  12253.     TA_S     *current;
  12254.     TA_SCREEN_S  *screen;
  12255.     Pos          *cursor_pos;
  12256. {
  12257.     int       dline;
  12258.     TA_S  *top_line,
  12259.           *ctmp;
  12260.     int    longest, i, j;
  12261.     char   buf1[MAX_SCREEN_COLS + 1];
  12262.     char   buf2[MAX_SCREEN_COLS + 1];
  12263.     char  *p, *q;
  12264.     int screen_width = ps->ttyo->screen_cols;
  12265.     Pos    cpos;
  12266.  
  12267.     cpos.row = HEADER_ROWS(ps); /* default return value */
  12268.  
  12269.     /* calculate top line of display */
  12270.     dline = 0;
  12271.     ctmp = top_line = first_taline(current);
  12272.     do
  12273.       if(((dline++) % (ps->ttyo->screen_rows - HEADER_ROWS(ps)
  12274.               - FOOTER_ROWS(ps))) == 0)
  12275.       top_line = ctmp;
  12276.     while(ctmp != current && (ctmp = next_taline(ctmp)));
  12277.  
  12278. #ifdef _WINDOWS
  12279.     /*
  12280.      * Figure out how far down the top line is from the top and how many
  12281.      * total lines there are.  Dumb to loop every time thru, but
  12282.      * there aren't that many lines, and it's cheaper than rewriting things
  12283.      * to maintain a line count in each structure...
  12284.      */
  12285.     for(dline = 0, ctmp = top_line; ctmp; ctmp = pre_taline(ctmp))
  12286.       dline++;
  12287.  
  12288.     scroll_setpos(dline - 1L);
  12289.  
  12290.     for(ctmp = next_taline(top_line); ctmp ; ctmp = next_taline(ctmp))
  12291.       dline++;
  12292.  
  12293.     scroll_setrange(dline);
  12294. #endif
  12295.  
  12296.  
  12297.     /* mangled body or new page, force redraw */
  12298.     if(ps->mangled_body || screen->top_line != top_line)
  12299.       screen->current = NULL;
  12300.     
  12301.     /* find length of longest line for nicer formatting */
  12302.     longest = 0;
  12303.     for(dline = 0, ctmp = top_line;
  12304.     dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
  12305.     dline++, ctmp = next_taline(ctmp)){
  12306.     int len;
  12307.  
  12308.     if(ctmp
  12309.        && !ctmp->print
  12310.        && ctmp->strvalue
  12311.        && longest < (len
  12312.            =strlen((char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  12313.                              ctmp->strvalue, NULL))))
  12314.       longest = len;
  12315.     }
  12316.  
  12317. #define LENGTH_OF_THAT_STRING 5   /* "[X]  " */
  12318.     longest = min(longest, ps->ttyo->screen_cols);
  12319.  
  12320.     /* loop thru painting what's needed */
  12321.     for(dline = 0, ctmp = top_line;
  12322.     dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
  12323.     dline++, ctmp = next_taline(ctmp)){
  12324.  
  12325.     /*
  12326.      * only fall thru painting if something needs painting...
  12327.      */
  12328.     if(!ctmp || !screen->current || ctmp == screen->current ||
  12329.                             ctmp == current){
  12330.         ClearLine(dline + HEADER_ROWS(ps));
  12331.         if(!ctmp || !ctmp->strvalue)
  12332.           continue;
  12333.     }
  12334.  
  12335.     p = buf1;
  12336.     if(ctmp == current){
  12337.         cpos.row = dline + HEADER_ROWS(ps);  /* col set below */
  12338.         StartInverse();
  12339.     }
  12340.  
  12341.     if(ctmp->print)
  12342.       j = 0;
  12343.     else
  12344.       j = LENGTH_OF_THAT_STRING;
  12345.  
  12346.     /*
  12347.      * Copy the value to a temp buffer expanding tabs, and
  12348.      * making sure not to write beyond screen right...
  12349.      */
  12350.     q = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  12351.                              ctmp->strvalue, NULL);
  12352.  
  12353.     for(i = 0; q[i] && j < ps->ttyo->screen_cols; i++){
  12354.         if(q[i] == ctrl('I')){
  12355.         do
  12356.           *p++ = SPACE;
  12357.         while(j < ps->ttyo->screen_cols && ((++j)&0x07));
  12358.               
  12359.         }
  12360.         else{
  12361.         *p++ = q[i];
  12362.         j++;
  12363.         }
  12364.     }
  12365.  
  12366.     *p = '\0';
  12367.  
  12368.     /* mark lines which have check marks */
  12369.     if(ctmp == current){
  12370.         if(screen->mode == ListMode){
  12371.         sprintf(buf2, "[%c]  %-*.*s",
  12372.             ctmp->checked ? 'X' : SPACE,
  12373.             longest, longest, buf1);
  12374.         cpos.col = 1; /* position on the X */
  12375.         }
  12376.         else{
  12377.         sprintf(buf2, "     %-*.*s",
  12378.             longest, longest, buf1);
  12379.         cpos.col = 5; /* 5 spaces before text */
  12380.         }
  12381.     }
  12382.     else{
  12383.         if(ctmp->print){
  12384.         int len;
  12385.         char *start_printing;
  12386.  
  12387.         memset((void *)buf2, '-', screen_width * sizeof(char));
  12388.         buf2[screen_width] = '\0';
  12389.         len = strlen(buf1);
  12390.         if(len > screen_width){
  12391.             len = screen_width;
  12392.             buf1[len] = '\0';
  12393.         }
  12394.  
  12395.         start_printing = buf2 + (screen_width - len) / 2;
  12396.         sprintf(start_printing, "%s", buf1);
  12397.         if(len < screen_width)
  12398.           start_printing[strlen(start_printing)] = '-';
  12399.         }
  12400.         else{
  12401.         if(screen->mode == ListMode)
  12402.               sprintf(buf2, "[%c]  %s", ctmp->checked ? 'X' : SPACE, buf1);
  12403.         else
  12404.               sprintf(buf2, "     %s", buf1);
  12405.         }
  12406.     }
  12407.  
  12408.     PutLine0(dline + HEADER_ROWS(ps), 0, buf2);
  12409.  
  12410.     if(ctmp == current)
  12411.       EndInverse();
  12412.     }
  12413.  
  12414.     ps->mangled_body = 0;
  12415.     screen->top_line = top_line;
  12416.     screen->current  = current;
  12417.     if(cursor_pos)
  12418.       *cursor_pos = cpos;
  12419.  
  12420.     return(cpos.row);
  12421. }
  12422.  
  12423.  
  12424. void
  12425. takeaddr_screen_redrawer_list()
  12426. {
  12427.     ps_global->mangled_body = 1;
  12428.     (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen,
  12429.     (Pos *)NULL);
  12430. }
  12431.  
  12432.  
  12433. void
  12434. takeaddr_screen_redrawer_single()
  12435. {
  12436.     ps_global->mangled_body = 1;
  12437.     (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen,
  12438.     (Pos *)NULL);
  12439. }
  12440.  
  12441.  
  12442. /*
  12443.  * new_taline - create new TA_S, zero it out, and insert it after current.
  12444.  *                NOTE current gets set to the new TA_S, too!
  12445.  */
  12446. TA_S *
  12447. new_taline(current)
  12448.     TA_S **current;
  12449. {
  12450.     TA_S *p;
  12451.  
  12452.     p = (TA_S *)fs_get(sizeof(TA_S));
  12453.     memset((void *)p, 0, sizeof(TA_S));
  12454.     if(current){
  12455.     if(*current){
  12456.         p->next         = (*current)->next;
  12457.         (*current)->next = p;
  12458.         p->prev         = *current;
  12459.         if(p->next)
  12460.           p->next->prev = p;
  12461.     }
  12462.  
  12463.     *current = p;
  12464.     }
  12465.  
  12466.     return(p);
  12467. }
  12468.  
  12469.  
  12470. void
  12471. free_taline(p)
  12472.     TA_S **p;
  12473. {
  12474.     if(p){
  12475.     if((*p)->addr)
  12476.       mail_free_address(&(*p)->addr);
  12477.  
  12478.     if((*p)->strvalue)
  12479.       fs_give((void **)&(*p)->strvalue);
  12480.  
  12481.     if((*p)->nickname)
  12482.       fs_give((void **)&(*p)->nickname);
  12483.  
  12484.     if((*p)->fullname)
  12485.       fs_give((void **)&(*p)->fullname);
  12486.  
  12487.     if((*p)->fcc)
  12488.       fs_give((void **)&(*p)->fcc);
  12489.  
  12490.     if((*p)->comment)
  12491.       fs_give((void **)&(*p)->comment);
  12492.  
  12493.     if((*p)->prev)
  12494.       (*p)->prev->next = (*p)->next;
  12495.  
  12496.     if((*p)->next)
  12497.       (*p)->next->prev = (*p)->prev;
  12498.  
  12499.     fs_give((void **)p);
  12500.     }
  12501. }
  12502.  
  12503.  
  12504. /*
  12505.  * Return the first selectable TakeAddr line.
  12506.  *
  12507.  * Args: q -- any line in the list
  12508.  */
  12509. TA_S *
  12510. first_sel_taline(q)
  12511.     TA_S *q;
  12512. {
  12513.     TA_S *first;
  12514.  
  12515.     if(q == NULL)
  12516.       return NULL;
  12517.  
  12518.     first = NULL;
  12519.     /* back up to the head of the list */
  12520.     while(q){
  12521.     first = q;
  12522.     q = pre_sel_taline(q);
  12523.     }
  12524.  
  12525.     /*
  12526.      * If first->skip_it, that means we were already past the first
  12527.      * legitimate line, so we have to look in the other direction.
  12528.      */
  12529.     if(first->skip_it)
  12530.       first = next_sel_taline(first);
  12531.     
  12532.     return(first);
  12533. }
  12534.  
  12535.  
  12536. /*
  12537.  * Return the last selectable TakeAddr line.
  12538.  *
  12539.  * Args: q -- any line in the list
  12540.  */
  12541. TA_S *
  12542. last_sel_taline(q)
  12543.     TA_S *q;
  12544. {
  12545.     TA_S *last;
  12546.  
  12547.     if(q == NULL)
  12548.       return NULL;
  12549.  
  12550.     last = NULL;
  12551.     /* go to the end of the list */
  12552.     while(q){
  12553.     last = q;
  12554.     q = next_sel_taline(q);
  12555.     }
  12556.  
  12557.     /*
  12558.      * If last->skip_it, that means we were already past the last
  12559.      * legitimate line, so we have to look in the other direction.
  12560.      */
  12561.     if(last->skip_it)
  12562.       last = pre_sel_taline(last);
  12563.     
  12564.     return(last);
  12565. }
  12566.  
  12567.  
  12568. /*
  12569.  * Return the first TakeAddr line, selectable or just printable.
  12570.  *
  12571.  * Args: q -- any line in the list
  12572.  */
  12573. TA_S *
  12574. first_taline(q)
  12575.     TA_S *q;
  12576. {
  12577.     TA_S *first;
  12578.  
  12579.     if(q == NULL)
  12580.       return NULL;
  12581.  
  12582.     first = NULL;
  12583.     /* back up to the head of the list */
  12584.     while(q){
  12585.     first = q;
  12586.     q = pre_taline(q);
  12587.     }
  12588.  
  12589.     /*
  12590.      * If first->skip_it, that means we were already past the first
  12591.      * legitimate line, so we have to look in the other direction.
  12592.      */
  12593.     if(first->skip_it && !first->print)
  12594.       first = next_taline(first);
  12595.     
  12596.     return(first);
  12597. }
  12598.  
  12599.  
  12600. /*
  12601.  * Find the first TakeAddr line which is checked, beginning with the
  12602.  * passed in line.
  12603.  *
  12604.  * Args: head -- A passed in TakeAddr line, usually will be the first
  12605.  *
  12606.  * Result: returns a pointer to the first checked line.
  12607.  *         NULL if no such line
  12608.  */
  12609. TA_S *
  12610. first_checked(head)
  12611.     TA_S *head;
  12612. {
  12613.     TA_S *p;
  12614.  
  12615.     p = head;
  12616.  
  12617.     for(p = head; p; p = next_sel_taline(p))
  12618.       if(p->checked && !p->skip_it)
  12619.     break;
  12620.  
  12621.     return(p);
  12622. }
  12623.  
  12624.  
  12625. /*
  12626.  * Form a list of strings which are addresses to go in a list.
  12627.  * These are entries in a list, so can be full rfc822 addresses.
  12628.  * The strings are allocated here.
  12629.  *
  12630.  * Args: head -- A passed in TakeAddr line, usually will be the first
  12631.  *
  12632.  * Result: returns an allocated array of pointers to allocated strings
  12633.  */
  12634. char **
  12635. list_of_checked(head)
  12636.     TA_S *head;
  12637. {
  12638.     TA_S  *p;
  12639.     int    count;
  12640.     char **list, **cur;
  12641.     ADDRESS *a;
  12642.     char buf[MAX_ADDR_EXPN+1];
  12643.  
  12644.     /* first count them */
  12645.     for(p = head, count = 0; p; p = next_sel_taline(p)){
  12646.     if(p->checked && !p->skip_it){
  12647.         if(p->frwrded){
  12648.         /*
  12649.          * Remove fullname, fcc, comment, and nickname since not
  12650.          * appropriate for list values.
  12651.          */
  12652.         if(p->fullname)
  12653.           fs_give((void **)&p->fullname);
  12654.         if(p->fcc)
  12655.           fs_give((void **)&p->fcc);
  12656.         if(p->comment)
  12657.           fs_give((void **)&p->comment);
  12658.         if(p->nickname)
  12659.           fs_give((void **)&p->nickname);
  12660.           
  12661.         for(a = p->addr; a; a = a->next)
  12662.           count++;
  12663.         }
  12664.         else{
  12665.         /*
  12666.          * Don't even attempt to include bogus addresses in
  12667.          * the list.  If the user wants to get at those, they
  12668.          * have to try taking only that single address.
  12669.          */
  12670.         if(p->addr && p->addr->host && p->addr->host[0] == '.')
  12671.           p->skip_it = 1;
  12672.         else
  12673.           count++;
  12674.         }
  12675.     }
  12676.     }
  12677.     
  12678.     /* allocate pointers */
  12679.     list = (char **)fs_get((count + 1) * sizeof(char *));
  12680.     memset((void *)list, 0, (count + 1) * sizeof(char *));
  12681.  
  12682.     cur = list;
  12683.  
  12684.     /* allocate and point to address strings */
  12685.     for(p = head; p; p = next_sel_taline(p)){
  12686.     if(p->checked && !p->skip_it){
  12687.         if(p->frwrded)
  12688.           for(a = p->addr; a; a = a->next)
  12689.         *cur++ = cpystr(addr_string(a, buf));
  12690.         else
  12691.           *cur++ = cpystr(p->strvalue);
  12692.     }
  12693.     }
  12694.  
  12695.     return(list);
  12696. }
  12697.  
  12698.  
  12699. /*
  12700.  * Free the list allocated above.
  12701.  *
  12702.  * Args: list -- The address of the list that was returned above.
  12703.  */
  12704. void
  12705. free_list(list)
  12706.     char ***list;
  12707. {
  12708.     char **p;
  12709.  
  12710.     for(p = *list; *p; p++)
  12711.       fs_give((void **)p);
  12712.  
  12713.     fs_give((void **)list);
  12714. }
  12715.  
  12716.  
  12717. /*
  12718.  * Execute command to take addresses out of message and put in the address book
  12719.  *
  12720.  * Args: ps     -- pine state
  12721.  *       msgmap -- the MessageMap
  12722.  *       agg    -- this is aggregate operation if set
  12723.  *
  12724.  * Result: The entry is added to an address book.
  12725.  */     
  12726. void
  12727. cmd_take_addr(ps, msgmap, agg)
  12728.     struct pine *ps;
  12729.     MSGNO_S     *msgmap;
  12730.     int          agg;
  12731. {
  12732.     long      i;
  12733.     ENVELOPE *env;
  12734.     int       how_many_selected,
  12735.           added,
  12736.           special_processing = 0,
  12737.           select_froms = 0;
  12738.     TA_S     *current,
  12739.          *prev_comment_line,
  12740.          *ta;
  12741.     BODY     **body_h,
  12742.           *body = NULL;
  12743.  
  12744.     dprint(2, (debugfile, "\n - taking address into address book - \n"));
  12745.  
  12746.     if(agg && !pseudo_selected(msgmap))
  12747.       return;
  12748.  
  12749.     if(mn_total_cur(msgmap) == 1)
  12750.       special_processing = 1;
  12751.  
  12752.     if(agg)
  12753.       select_froms = 1;
  12754.  
  12755.     ps->mangled_footer = 1;
  12756.     how_many_selected = 0;
  12757.     current = NULL;
  12758.  
  12759.     /* this is a non-selectable label */
  12760.     current = fill_in_ta(¤t, (ADDRESS *)NULL, 0,
  12761.            " These entries are taken from the attachments ");
  12762.     prev_comment_line = current;
  12763.  
  12764.     /*
  12765.      * Add addresses from special attachments for each message.
  12766.      */
  12767.     added = 0;
  12768.     for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
  12769.     env = mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
  12770.                   &body);
  12771.     if(!env){
  12772.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  12773.            "Can't take address into address book. Error accessing folder");
  12774.         goto bomb;
  12775.     }
  12776.  
  12777.     added += process_special_abook_attachments(ps->mail_stream,
  12778.                            mn_m2raw(msgmap, i),
  12779.                            body, body, ¤t);
  12780.     }
  12781.  
  12782.     /*
  12783.      * add a comment line to separate attachment address lines
  12784.      * from header address lines
  12785.      */
  12786.     if(added > 0){
  12787.     current = fill_in_ta(¤t, (ADDRESS *)NULL, 0,
  12788.            " These entries are taken from the msg headers ");
  12789.     prev_comment_line = current;
  12790.     select_froms = 0;
  12791.     }
  12792.     else{  /* turn off header comment, and no separator comment */
  12793.     prev_comment_line->print = 0;
  12794.     prev_comment_line        = NULL;
  12795.     }
  12796.  
  12797.     body = NULL;
  12798.     /*
  12799.      * Add addresses from headers of messages.
  12800.      */
  12801.     for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
  12802.  
  12803.     if(special_processing)
  12804.       body_h = &body;
  12805.     else
  12806.       body_h = NULL;
  12807.  
  12808.     env = mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
  12809.                   body_h);
  12810.     if(!env){
  12811.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  12812.            "Can't take address into address book. Error accessing folder");
  12813.         goto bomb;
  12814.     }
  12815.  
  12816.     added = add_addresses_to_talist(ps, i, "from", ¤t,
  12817.                     env->from, select_froms);
  12818.     if(select_froms)
  12819.       how_many_selected += added;
  12820.  
  12821.     (void)add_addresses_to_talist(ps,i,"reply-to",¤t,env->reply_to,0);
  12822.     (void)add_addresses_to_talist(ps, i, "to", ¤t, env->to, 0);
  12823.     (void)add_addresses_to_talist(ps, i, "cc", ¤t, env->cc, 0);
  12824.     (void)add_addresses_to_talist(ps, i, "bcc", ¤t, env->bcc, 0);
  12825.     }
  12826.     
  12827.     /*
  12828.      * Check to see if we added an explanatory line about the
  12829.      * header addresses but no header addresses.  If so, remove the
  12830.      * explanatory line.
  12831.      */
  12832.     if(prev_comment_line){
  12833.     how_many_selected -=
  12834.         eliminate_dups_and_us(first_sel_taline(current));
  12835.     for(ta = prev_comment_line->next; ta; ta = ta->next)
  12836.       if(!ta->skip_it)
  12837.         break;
  12838.  
  12839.     /* all entries were skip_it entries, turn off print */
  12840.     if(!ta){
  12841.         prev_comment_line->print = 0;
  12842.         prev_comment_line        = NULL;
  12843.     }
  12844.     }
  12845.  
  12846.     /*
  12847.      * If there's only one message we let the user view it using ^T
  12848.      * from the header editor.
  12849.      */
  12850.     if(special_processing && env && body){
  12851.     msgno_for_pico_callback = mn_m2raw(msgmap, mn_first_cur(msgmap));
  12852.     body_for_pico_callback  = body;
  12853.     env_for_pico_callback   = env;
  12854.     }
  12855.     else{
  12856.     env_for_pico_callback   = NULL;
  12857.     body_for_pico_callback  = NULL;
  12858.     }
  12859.  
  12860.     /*
  12861.      * If a single message, also look for addresses in the text of the body.
  12862.      */
  12863.     added = 0;
  12864.     if(special_processing && body){
  12865.     current = fill_in_ta(¤t, (ADDRESS *)NULL, 0,
  12866.     " Below this line are some possibilities taken from the text of the msg ");
  12867.     prev_comment_line = current;
  12868.     added = grab_addrs_from_body(ps->mail_stream,
  12869.                      mn_m2raw(msgmap, mn_first_cur(msgmap)),
  12870.                      body, ¤t);
  12871.     if(added == 0){
  12872.         prev_comment_line->print = 0;
  12873.         prev_comment_line        = NULL;
  12874.     }
  12875.     }
  12876.  
  12877.     current = first_sel_taline(current);
  12878.     how_many_selected -= eliminate_dups_and_us(current);
  12879.  
  12880.     /*
  12881.      * Check to see if we added some below the comment line and
  12882.      * then decided they're all dups.  In that case, we don't
  12883.      * want to print out the line below the header list.
  12884.      */
  12885.     if(added > 0 && prev_comment_line){
  12886.     for(ta = prev_comment_line->next; ta; ta = ta->next)
  12887.       if(!ta->skip_it)
  12888.         break;
  12889.  
  12890.     /* all entries were skip_it entries, turn off print */
  12891.     if(!ta){
  12892.         prev_comment_line->print = 0;
  12893.         prev_comment_line        = NULL;
  12894.     }
  12895.     }
  12896.  
  12897.     takeaddr_screen(ps, current, how_many_selected,
  12898.             agg ? ListMode : SingleMode);
  12899.  
  12900. bomb:
  12901.     env_for_pico_callback   = NULL;
  12902.     body_for_pico_callback  = NULL;
  12903.  
  12904.     if(agg)
  12905.       restore_selected(msgmap);
  12906. }
  12907.  
  12908.  
  12909. int
  12910. add_addresses_to_talist(ps, msgno, field, old_current, adrlist, checked)
  12911.     struct pine *ps;
  12912.     long         msgno;
  12913.     char        *field;
  12914.     TA_S       **old_current;
  12915.     ADDRESS     *adrlist;
  12916.     int          checked;
  12917. {
  12918.     ADDRESS *addr;
  12919.     int      added = 0;
  12920.  
  12921.     for(addr = adrlist; addr; addr = addr->next){
  12922.     if(addr->host && addr->host[0] == '.'){
  12923.         char *h, *fields[2];
  12924.  
  12925.         fields[0] = field;
  12926.         fields[1] = NULL;
  12927.         if(h = xmail_fetchheader_lines(ps->mail_stream, msgno, fields)){
  12928.         char *p, fname[32];
  12929.  
  12930.         sprintf(fname, "%s:", field);
  12931.         for(p = h; p = strstr(p, fname); )
  12932.           rplstr(p, strlen(fname), "");   /* strip field strings */
  12933.         
  12934.         sqznewlines(h);                   /* blat out CR's & LF's */
  12935.         for(p = h; p = strchr(p, TAB); )
  12936.           *p++ = ' ';                     /* turn TABs to space */
  12937.         
  12938.         if(*h){
  12939.             unsigned long l;
  12940.             ADDRESS *new_addr;
  12941.  
  12942.             new_addr = (ADDRESS *)fs_get(sizeof(ADDRESS));
  12943.             memset(new_addr, 0, sizeof(ADDRESS));
  12944.  
  12945.             /*
  12946.              * Base64 armor plate the gunk to protect against
  12947.              * c-client quoting in output.
  12948.              */
  12949.             p = (char *)rfc822_binary((void *)h,
  12950.                           (unsigned long)strlen(h), &l);
  12951.             new_addr->mailbox = (char *)fs_get(strlen(p) + 4);
  12952.             sprintf(new_addr->mailbox, "&%s", p);
  12953.             fs_give((void **)&p);
  12954.             new_addr->host = cpystr(".RAW-FIELD.");
  12955.             fill_in_ta(old_current, new_addr, 0, (char *)NULL);
  12956.             mail_free_address(&new_addr);
  12957.         }
  12958.  
  12959.         fs_give((void **)&h);
  12960.         }
  12961.  
  12962.         return(0);
  12963.     }
  12964.     }
  12965.  
  12966.     /* no syntax errors in list, add them all */
  12967.     for(addr = adrlist; addr; addr = addr->next){
  12968.     fill_in_ta(old_current, addr, checked, (char *)NULL);
  12969.     added++;
  12970.     }
  12971.  
  12972.     return(added);
  12973. }
  12974.  
  12975.  
  12976. /*
  12977.  * Look for Application/directory attachments and process them.
  12978.  * Return number of lines added to the ta_list.
  12979.  */
  12980. int
  12981. process_special_abook_attachments(stream, msgno, root, body, ta_list)
  12982.     MAILSTREAM *stream;
  12983.     long        msgno;
  12984.     BODY       *root;
  12985.     BODY       *body;
  12986.     TA_S      **ta_list;
  12987. {
  12988.     ADDRESS   *addrlist;
  12989.     char      *addrs,        /* comma-separated list of addresses */
  12990.           *fakedomain = "@",
  12991.           *value,
  12992.           *encoded,
  12993.           *nickname,
  12994.           *fullname,
  12995.           *fcc,
  12996.           *tag,
  12997.           *num = NULL,
  12998.           *defaulttype = NULL,
  12999.           *charset = NULL,
  13000.           *altcharset,
  13001.           *comment,
  13002.          **lines = NULL,
  13003.          **ll;
  13004.     size_t     space;
  13005.     int        used = 0,
  13006.            selected = 0;
  13007.     PART      *part;
  13008.     PARAMETER *parm;
  13009.  
  13010.     /*
  13011.      * Look through all the subparts searching for our special type.
  13012.      */
  13013.     if(body && body->type == TYPEMULTIPART){
  13014.     for(part = body->contents.part; part; part = part->next)
  13015.       selected += process_special_abook_attachments(stream, msgno, root,
  13016.                               &part->body, ta_list);
  13017.     }
  13018.     /* only look in rfc822 subtype of type message */
  13019.     else if(body && body->type == TYPEMESSAGE
  13020.         && body->subtype && !strucmp(body->subtype, "rfc822")){
  13021.     selected += process_special_abook_attachments(stream, msgno, root,
  13022.                           body->contents.msg.body, ta_list);
  13023.     }
  13024.     /* found one */
  13025.     else if(body && body->type == TYPEAPPLICATION
  13026.             && body->subtype && !strucmp(body->subtype, "directory")){
  13027.  
  13028.     /*
  13029.      * The Application/directory type has a possible parameter called
  13030.      * "defaulttype" that we need to look for.  There is also a
  13031.      * possible default "charset".  We don't care about any of the
  13032.      * other parameters.
  13033.      */
  13034.     for(parm = body->parameter; parm; parm = parm->next)
  13035.       if(!strucmp("defaulttype", parm->attribute))
  13036.         break;
  13037.     
  13038.     if(parm)
  13039.       defaulttype = parm->value;
  13040.  
  13041.     /* ...and look for possible charset parameter */
  13042.     for(parm = body->parameter; parm; parm = parm->next)
  13043.       if(!strucmp("charset", parm->attribute))
  13044.         break;
  13045.     
  13046.     if(parm)
  13047.       charset = parm->value;
  13048.  
  13049.     num = partno(root, body);
  13050.     lines = detach_abook_att(stream, msgno, body, num);
  13051.     if(num)
  13052.       fs_give((void **)&num);
  13053.  
  13054.     nickname = fullname = comment = fcc = NULL;
  13055. #define CHUNK (size_t)500
  13056.     space = CHUNK;
  13057.     /* make comma-separated list of email addresses in addrs */
  13058.     addrs = (char *)fs_get((space+1) * sizeof(char));
  13059.     *addrs = '\0';
  13060.     for(ll = lines; ll && *ll && **ll; ll++){
  13061.         altcharset = NULL;
  13062.         value = getaltcharset(*ll, &tag, &altcharset);
  13063.  
  13064.         /* support default tag */
  13065.         if(*tag == '\0' && defaulttype){
  13066.         fs_give((void **)&tag);
  13067.         tag = cpystr(defaulttype);
  13068.         }
  13069.  
  13070.         /* add another address to addrs */
  13071.         if(!strucmp(tag, "email")){
  13072.         if(value && *value){
  13073.             encoded = encode_fullname_of_addrstring(value,
  13074.             (altcharset && *altcharset) ? altcharset
  13075.                             : (charset && *charset)
  13076.                              ? charset
  13077.                              : ps_global->VAR_CHAR_SET);
  13078.             /* allocate more space */
  13079.             if((used + strlen(encoded) + 1) > space){
  13080.             space += CHUNK;
  13081.             fs_resize((void **)&addrs, (space+1) * sizeof(char));
  13082.             }
  13083.  
  13084.             if(*addrs)
  13085.               strcat(addrs, ",");
  13086.  
  13087.             strcat(addrs, encoded);
  13088.             used += (strlen(encoded) + 1);
  13089.             if(encoded)
  13090.               fs_give((void **)&encoded);
  13091.         }
  13092.         }
  13093.         else if(!strucmp(tag, "misc")){
  13094.         if(value && *value){
  13095.             encoded = rfc1522_encode(tmp_20k_buf,
  13096.                          (unsigned char *)value,
  13097.             (altcharset && *altcharset) ? altcharset
  13098.                             : (charset && *charset)
  13099.                              ? charset
  13100.                              : ps_global->VAR_CHAR_SET);
  13101.             comment = cpystr(encoded);
  13102.         }
  13103.         }
  13104.         else if(!strucmp(tag, "x-fcc")){
  13105.         if(value && *value)
  13106.           fcc = cpystr(value);
  13107.         }
  13108.         else if(!strucmp(tag, "cn")){
  13109.         if(value && *value){
  13110.             encoded = rfc1522_encode(tmp_20k_buf,
  13111.                          (unsigned char *)value,
  13112.             (altcharset && *altcharset) ? altcharset
  13113.                             : (charset && *charset)
  13114.                              ? charset
  13115.                              : ps_global->VAR_CHAR_SET);
  13116.             fullname = cpystr(encoded);
  13117.         }
  13118.         }
  13119.         /* suggested nickname */
  13120.         else if(!strucmp(tag, "x-nickname")){
  13121.         if(value && *value)
  13122.           nickname = cpystr(value);
  13123.         }
  13124.  
  13125.         if(tag)
  13126.           fs_give((void **)&tag);
  13127.  
  13128.         if(altcharset)
  13129.           fs_give((void **)&altcharset);
  13130.     }
  13131.  
  13132.     /*
  13133.      * Parse it into an addrlist, which is what fill_in_ta wants
  13134.      * to see.
  13135.      */
  13136.     addrlist = NULL;
  13137.     rfc822_parse_adrlist(&addrlist, addrs, fakedomain);
  13138.     if(addrs)
  13139.       fs_give((void **)&addrs);
  13140.  
  13141.     selected++;
  13142.     *ta_list = fill_in_ta(ta_list, addrlist, 0,
  13143.                   fullname ? fullname : "Forwarded Entry");
  13144.     (*ta_list)->nickname = nickname;
  13145.     (*ta_list)->fullname = fullname;
  13146.     (*ta_list)->comment  = comment;
  13147.     (*ta_list)->fcc      = fcc;
  13148.  
  13149.     if(lines)
  13150.       free_list(&lines);
  13151.  
  13152.     if(addrlist)
  13153.       mail_free_address(&addrlist);
  13154.     }
  13155.  
  13156.     return(selected);
  13157. }
  13158.  
  13159.  
  13160. /*
  13161.  * Look through line which is supposed to look like
  13162.  *
  13163.  *  type;charset=iso-8859-1;encoding=base64: stuff
  13164.  *
  13165.  * Type might be email, or x-nickname, ...  It is optional because of the
  13166.  * defaulttype parameter.  The semicolon and colon are special chars.  Each
  13167.  * parameter in this line is a semicolon followed by the parameter type "="
  13168.  * the parameter value.  Parameters are optional, too.  There is always a colon,
  13169.  * followed by stuff.  Whitespace can be everywhere up to where stuff starts.
  13170.  * There is also an optional <group> "." preceding the type, which we will
  13171.  * ignore.  It's for grouping related lines.
  13172.  *
  13173.  * BUG:  Ignoring encoding for now.  It shouldn't happen in any reasonable
  13174.  *       entry we're concerned with. If it does, it's not the end of the world.
  13175.  *
  13176.  * Args: line     -- the line to look at
  13177.  *       type     -- this is a return value, and is an allocated copy of
  13178.  *                    the type, freed by the caller
  13179.  *       alt      -- this is a return value, and is an allocated copy of
  13180.  *                    the value of the alternate charset, to be freed by
  13181.  *                    the caller.  For example, this might be "iso-8859-2".
  13182.  *
  13183.  * Return value: a pointer to the start of stuff, or NULL if the syntax
  13184.  * isn't right.  It's possible for stuff to be equal to "".
  13185.  */
  13186. char *
  13187. getaltcharset(line, type, alt)
  13188.     char  *line;
  13189.     char **type;
  13190.     char **alt;
  13191. {
  13192.     char *p, *q, *left_semi, *group_dot, *colon, *start_of_cset, tmpsave;
  13193.     static char *cset = "charset";
  13194.  
  13195.     if(type)
  13196.       *type = NULL;
  13197.  
  13198.     if(alt)
  13199.       *alt = NULL;
  13200.  
  13201.     colon = strindex(line, ':');
  13202.     if(!colon)
  13203.       return NULL;
  13204.  
  13205.     left_semi = strindex(line, ';');
  13206.     if(left_semi && left_semi > colon)
  13207.       left_semi = NULL;
  13208.     
  13209.     group_dot = strindex(line, '.');
  13210.     if(group_dot && (group_dot > colon || left_semi && group_dot > left_semi))
  13211.       group_dot = NULL;
  13212.  
  13213.     /*
  13214.      * Type is everything up to the semicolon, or the colon if no semicolon.
  13215.      * However, we want to skip optional <group> ".".
  13216.      */
  13217.     if(type){
  13218.     q = left_semi ? left_semi : colon;
  13219.     tmpsave = *q;
  13220.     *q = '\0';
  13221.     *type = cpystr(group_dot ? group_dot+1 : line);
  13222.     *q = tmpsave;
  13223.     sqzspaces(*type);
  13224.     }
  13225.  
  13226. #define SKIP_WHITESPACE(p) do{while(*p && isspace((unsigned char)*p))p++;}while(0)
  13227.  
  13228.     if(left_semi && alt
  13229.        && (p = srchstr(left_semi+1, cset))
  13230.        && p < colon){
  13231.     p += strlen(cset);
  13232.     SKIP_WHITESPACE(p);
  13233.     if(*p++ == '='){
  13234.         SKIP_WHITESPACE(p);
  13235.         if(p < colon){
  13236.         start_of_cset = p;
  13237.         p++;
  13238.         while(p < colon && !isspace((unsigned char)*p) && *p != ';')
  13239.           p++;
  13240.         
  13241.         tmpsave = *p;
  13242.         *p = '\0';
  13243.         *alt = cpystr(start_of_cset);
  13244.         *p = tmpsave;
  13245.         }
  13246.     }
  13247.     }
  13248.  
  13249.     p = colon + 1;
  13250.     SKIP_WHITESPACE(p);
  13251.  
  13252.     return(p);
  13253. }
  13254.  
  13255.  
  13256. /*
  13257.  * Fetch this body part, content-decode it if necessary, split it into
  13258.  * lines, and return the lines in an allocated array, which is freed
  13259.  * by the caller.  Folded lines are replaced by single long lines.
  13260.  */
  13261. char **
  13262. detach_abook_att(stream, msgno, body, partnum)
  13263.     MAILSTREAM *stream;
  13264.     long        msgno;
  13265.     BODY       *body;
  13266.     char       *partnum;
  13267. {
  13268.     unsigned long length;
  13269.     char    *text  = NULL, /* raw text */
  13270.         *dtext = NULL, /* content-decoded text */
  13271.        **res   = NULL, /* array of decoded lines */
  13272.         *lptr,         /* line pointer */
  13273.         *next_line;
  13274.     int      i, count;
  13275.  
  13276.     /* make our own copy of text so we can change it */
  13277.     text = cpystr(mail_fetchbody(stream, msgno, partnum, &length));
  13278.  
  13279.     /* decode the text */
  13280.     switch(body->encoding){
  13281.       default:
  13282.       case ENC7BIT:
  13283.       case ENC8BIT:
  13284.       case ENCBINARY:
  13285.     dtext = text;
  13286.     break;
  13287.  
  13288.       case ENCBASE64:
  13289.     dtext = (char *)rfc822_base64((unsigned char *)text,
  13290.                       (unsigned long)strlen(text),
  13291.                       &length);
  13292.     if(text)
  13293.       fs_give((void **)&text);
  13294.  
  13295.     break;
  13296.  
  13297.       case ENCQUOTEDPRINTABLE:
  13298.     dtext = (char *)rfc822_qprint((unsigned char *)text,
  13299.                       (unsigned long)strlen(text),
  13300.                       &length);
  13301.     if(text)
  13302.       fs_give((void **)&text);
  13303.  
  13304.     break;
  13305.     }
  13306.  
  13307.     /* count the lines */
  13308.     next_line = dtext;
  13309.     count = 0;
  13310.     for(lptr = next_line; lptr && *lptr; lptr = next_line){
  13311.     for(next_line = lptr; *next_line; next_line++){
  13312.         /*
  13313.          * Find end of current line.  This should really be CRLF,
  13314.          * but we'll accept either CR or LF.
  13315.          */
  13316.         if(*next_line == '\r' || *next_line == '\n'){
  13317.         next_line++;
  13318.         /* Find start of next line */
  13319.         while(*next_line && (*next_line == '\r' || *next_line == '\n'))
  13320.           next_line++;
  13321.         
  13322.         /* not a folded line, count it */
  13323.         if(!*next_line || (*next_line != SPACE && *next_line != TAB))
  13324.           break;
  13325.         }
  13326.     }
  13327.  
  13328.     count++;
  13329.     }
  13330.  
  13331.     /* allocate space for resulting array of lines */
  13332.     res = (char **)fs_get((count + 1) * sizeof(char *));
  13333.     memset((void *)res, 0, (count + 1) * sizeof(char *));
  13334.     next_line = dtext;
  13335.     for(i=0, lptr=next_line; lptr && *lptr && i < count; lptr=next_line, i++){
  13336.     /*
  13337.      * Move next_line to start of next line and null terminate
  13338.      * current line.
  13339.      */
  13340.     for(next_line = lptr; *next_line; next_line++){
  13341.         /*
  13342.          * Find end of current line.  This should really be CRLF,
  13343.          * but we'll accept either CR or LF.
  13344.          */
  13345.         if(*next_line == '\r' || *next_line == '\n'){
  13346.         next_line++;
  13347.         /* Find start of next line */
  13348.         while(*next_line && (*next_line == '\r' || *next_line == '\n'))
  13349.           next_line++;
  13350.         
  13351.         /* not a folded line, terminate it */
  13352.         if(!*next_line || (*next_line != SPACE && *next_line != TAB)){
  13353.             *(next_line-1) = '\0';
  13354.             break;
  13355.         }
  13356.         }
  13357.     }
  13358.  
  13359.     sqznewlines(lptr);  /* turns folded lines into long lines, and
  13360.                 eliminates trailing CR/LF */
  13361.     res[i] = cpystr(lptr);
  13362.     }
  13363.  
  13364.     if(dtext)
  13365.       fs_give((void **)&dtext);
  13366.  
  13367.     res[count] = '\0';
  13368.     return(res);
  13369. }
  13370.  
  13371.  
  13372. /*
  13373.  * Look for possible addresses in the first text part of a message for
  13374.  * use by TakeAddr command.
  13375.  * Returns the number of TA lines added.
  13376.  */
  13377. int
  13378. grab_addrs_from_body(stream, msgno, body, ta_list)
  13379.     MAILSTREAM *stream;
  13380.     long        msgno;
  13381.     BODY       *body;
  13382.     TA_S      **ta_list;
  13383. {
  13384. #define MAXLINESZ 2000
  13385.     char       line[MAXLINESZ + 1];
  13386.     STORE_S   *so;
  13387.     gf_io_t    pc;
  13388.     SourceType src = CharStar;
  13389.     int        added = 0;
  13390.     char      *at, *colon, *p, *next_p, *start, *end, *fn_end;
  13391.     char      *tmp_a_string, *tmp_personal;
  13392.     char       save_end, save_fn_end;
  13393.     char      *fakedomain = "@";
  13394.     ADDRESS   *addr;
  13395.  
  13396.     /*
  13397.      * If it is text/plain or it is multipart with a first part of text/plain,
  13398.      * we want to continue, else forget it.
  13399.      */
  13400.     if(!((body->type == TYPETEXT && body->subtype &&
  13401.         !strucmp(body->subtype, "plain"))
  13402.                   ||
  13403.          (body->type == TYPEMULTIPART && body->contents.part
  13404.         && body->contents.part->body.type == TYPETEXT
  13405.         && !strucmp(body->contents.part->body.subtype, "plain"))))
  13406.       return 0;
  13407.  
  13408. #ifdef DOS
  13409.     src = TmpFileStar;
  13410. #endif
  13411.  
  13412.     if((so = so_get(src, NULL, EDIT_ACCESS)) == NULL)
  13413.       return 0;
  13414.  
  13415.     gf_set_so_writec(&pc, so);
  13416.     if(!get_body_part_text(stream, body, msgno, "1", pc, NULL, NULL)){
  13417.     so_give(&so);
  13418.     return 0;
  13419.     }
  13420.  
  13421.     so_seek(so, 0L, 0);
  13422.  
  13423.     while(get_line_of_message(so, line, MAXLINESZ + 1)){
  13424.  
  13425.     colon = NULL;
  13426.  
  13427.     /* process each @ in the line */
  13428.     for(p = (char *)line; at = strindex(p, '@'); p = next_p){
  13429.  
  13430.         next_p = at + 1;
  13431.  
  13432.         /* find start of address */
  13433.         start = at;
  13434.         while(start > p && !isspace((unsigned char)*(start-1)))
  13435.           start--;
  13436.  
  13437.         /* remove parens */
  13438.         if(*start == '(')
  13439.           start++;
  13440.         
  13441.         /* find end of address */
  13442.         end = at;
  13443.         while(*end != '\0' && !isspace((unsigned char)*end))
  13444.           end++;
  13445.  
  13446.         save_end = *end;
  13447.         *end = '\0';
  13448.  
  13449.         end--;
  13450.         /*
  13451.          * remove periods, commas, closing parens, question
  13452.          * marks, and exclamation marks
  13453.          */
  13454.         if(end > start &&
  13455.                (*end == '.' ||
  13456.                 *end == ',' ||
  13457.             *end == ')' ||
  13458.             *end == '!' ||
  13459.             *end == '?')){
  13460.         *(end + 1) = save_end;
  13461.         save_end = *end;
  13462.         *end = '\0';
  13463.         }
  13464.         else
  13465.           end++;
  13466.  
  13467.         tmp_personal = NULL;
  13468.  
  13469.         if(*start == '<' && end > start && *(end - 1) == '>'){
  13470.         /*
  13471.          * Take a shot at looking for full name
  13472.          * If we can find a colon maybe we've got a header line
  13473.          * embedded in the body.
  13474.          *
  13475.          * Or, if this isn't the first one in the line we'll just
  13476.          * go back to a comma.
  13477.          */
  13478.         if(colon && (colon = strrindex(p, ',')) ||
  13479.            (colon = strrindex(p, ':'))){
  13480.             /* skip white space after colon */
  13481.             colon++;
  13482.             while(colon < start && isspace((unsigned char)*colon))
  13483.               colon++;
  13484.  
  13485.             if(colon < start){
  13486.             /* remove white space between fullname and start */
  13487.             fn_end = start - 1;
  13488.             while(fn_end > colon
  13489.                   && isspace((unsigned char)*(fn_end - 1)))
  13490.               fn_end--;
  13491.  
  13492.             save_fn_end = *fn_end;
  13493.             *fn_end = '\0';
  13494.             tmp_personal = cpystr(colon);
  13495.             *fn_end = save_fn_end;
  13496.             }
  13497.         }
  13498.         }
  13499.  
  13500.         /* rfc822_parse_adrlist feels free to destroy input so send copy */
  13501.         tmp_a_string = cpystr(start);
  13502.         *end = save_end;
  13503.         addr = NULL;
  13504.         ps_global->c_client_error[0] = '\0';
  13505.         rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
  13506.         if(tmp_a_string)
  13507.           fs_give((void **)&tmp_a_string);
  13508.  
  13509.         if(tmp_personal){
  13510.         if(addr){
  13511.             if(addr->personal)
  13512.               fs_give((void **)&addr->personal);
  13513.  
  13514.             addr->personal = tmp_personal;
  13515.         }
  13516.         else
  13517.           fs_give((void **)&tmp_personal);
  13518.         }
  13519.  
  13520.         if((addr && addr->host && addr->host[0] == '@') ||
  13521.            ps_global->c_client_error[0]){
  13522.         mail_free_address(&addr);
  13523.         continue;
  13524.         }
  13525.  
  13526.         if(addr){
  13527.         added++;
  13528.         *ta_list = fill_in_ta(ta_list, addr, 0, (char *)NULL);
  13529.         mail_free_address(&addr);
  13530.         }
  13531.     }
  13532.     }
  13533.  
  13534.     so_give(&so);
  13535.     return(added);
  13536. }
  13537.  
  13538.  
  13539. /*
  13540.  * Get the next line of the object pointed to by source.
  13541.  * Skips empty lines.
  13542.  * Linebuf is a passed in buffer to put the line in.
  13543.  * Linebuf is null terminated and \r and \n chars are removed.
  13544.  * 0 is returned when there is no more input.
  13545.  */
  13546. int
  13547. get_line_of_message(source, linebuf, linebuflen)
  13548.     STORE_S *source;
  13549.     char    *linebuf;
  13550.     int      linebuflen;
  13551. {
  13552.     int pos = 0;
  13553.     unsigned char c;
  13554.  
  13555.     if(linebuflen < 2)
  13556.       return 0;
  13557.  
  13558.     while(so_readc(&c, source)){
  13559.     if(c == '\n' || c == '\r'){
  13560.       if(pos > 0)
  13561.         break;
  13562.     }
  13563.     else{
  13564.         linebuf[pos++] = c;
  13565.         if(pos >= linebuflen - 2)
  13566.           break;
  13567.     }
  13568.     }
  13569.  
  13570.     linebuf[pos] = '\0';
  13571.  
  13572.     return(pos);
  13573. }
  13574.  
  13575.  
  13576. /*
  13577.  * Copy the first address in list a and return it in allocated memory.
  13578.  */
  13579. ADDRESS *
  13580. copyaddr(a)
  13581.     ADDRESS *a;
  13582. {
  13583.     ADDRESS *new;
  13584.  
  13585.     new = mail_newaddr();
  13586.     if(a->personal)
  13587.       new->personal = cpystr(a->personal);
  13588.  
  13589.     if(a->adl)
  13590.       new->adl      = cpystr(a->adl);
  13591.  
  13592.     if(a->mailbox)
  13593.       new->mailbox  = cpystr(a->mailbox);
  13594.  
  13595.     if(a->host)
  13596.       new->host     = cpystr(a->host);
  13597.  
  13598.     new->next = NULL;
  13599.  
  13600.     return(new);
  13601. }
  13602.  
  13603.  
  13604. /*
  13605.  * Copy the whole list a.
  13606.  */
  13607. ADDRESS *
  13608. copyaddrlist(a)
  13609.     ADDRESS *a;
  13610. {
  13611.     ADDRESS *new = NULL, *head = NULL, *current;
  13612.  
  13613.     for(; a; a = a->next){
  13614.     new = mail_newaddr();
  13615.     if(!head)
  13616.       head = current = new;
  13617.     else{
  13618.         current->next = new;
  13619.         current = new;
  13620.     }
  13621.  
  13622.     if(a->personal)
  13623.       new->personal = cpystr(a->personal);
  13624.  
  13625.     if(a->adl)
  13626.       new->adl      = cpystr(a->adl);
  13627.  
  13628.     if(a->mailbox)
  13629.       new->mailbox  = cpystr(a->mailbox);
  13630.  
  13631.     if(a->host)
  13632.       new->host     = cpystr(a->host);
  13633.     }
  13634.  
  13635.     if(new)
  13636.       new->next = NULL;
  13637.  
  13638.     return(head);
  13639. }
  13640.  
  13641.  
  13642. /*
  13643.  * Inserts a new entry based on addr in the TA list.
  13644.  *
  13645.  * Args: old_current -- the TA list
  13646.  *              addr -- the address for this line
  13647.  *           checked -- start this line out checked
  13648.  *      print_string -- if non-null, this line is informational and is just
  13649.  *                       printed out, it can't be selected
  13650.  */
  13651. TA_S *
  13652. fill_in_ta(old_current, addr, checked, print_string)
  13653.     TA_S    **old_current;
  13654.     ADDRESS  *addr;
  13655.     int       checked;
  13656.     char     *print_string;
  13657. {
  13658.     TA_S *new_current;
  13659.  
  13660.     /* c-client convention for group syntax, which we don't want to deal with */
  13661.     if(!print_string && (!addr || !addr->mailbox || !addr->host))
  13662.       new_current = *old_current;
  13663.     else{
  13664.  
  13665.     new_current           = new_taline(old_current);
  13666.     if(print_string && addr){
  13667.         new_current->frwrded  = 1;
  13668.         new_current->skip_it  = 0;
  13669.         new_current->print    = 0;
  13670.         new_current->checked  = checked;
  13671.         new_current->addr     = copyaddrlist(addr);
  13672.         new_current->strvalue = cpystr(print_string);
  13673.     }
  13674.     else if(print_string){
  13675.         new_current->frwrded  = 0;
  13676.         new_current->skip_it  = 1;
  13677.         new_current->print    = 1;
  13678.         new_current->checked  = 0;
  13679.         new_current->addr     = 0;
  13680.         new_current->strvalue = cpystr(print_string);
  13681.     }
  13682.     else{
  13683.         char buf[MAX_ADDR_EXPN+1];
  13684.  
  13685.         new_current->frwrded  = 0;
  13686.         new_current->skip_it  = 0;
  13687.         new_current->print    = 0;
  13688.         new_current->checked  = checked;
  13689.         new_current->addr     = copyaddr(addr);
  13690.         if(addr->host[0] == '.')
  13691.           new_current->strvalue
  13692.           = cpystr("Error in address (ok to try Take anyway)");
  13693.         else
  13694.           new_current->strvalue
  13695.                   = cpystr(addr_string(new_current->addr,buf));
  13696.     }
  13697.     }
  13698.  
  13699.     return(new_current);
  13700. }
  13701.  
  13702.  
  13703. int
  13704. eliminate_dups_and_us(list)
  13705.     TA_S *list;
  13706. {
  13707.     return(eliminate_dups_and_maybe_us(list, 1));
  13708. }
  13709.  
  13710.  
  13711. int
  13712. eliminate_dups_but_not_us(list)
  13713.     TA_S *list;
  13714. {
  13715.     return(eliminate_dups_and_maybe_us(list, 0));
  13716. }
  13717.  
  13718.  
  13719. /*
  13720.  * Look for dups in list and mark them so we'll skip them.
  13721.  *
  13722.  * We also throw out any addresses that are us (if us_too is set), since
  13723.  * we won't usually want to take ourselves to the addrbook.
  13724.  * On the otherhand, if there is nothing but us, we leave it.
  13725.  *
  13726.  * Returns the number of dups eliminated that were also selected.
  13727.  */
  13728. int
  13729. eliminate_dups_and_maybe_us(list, us_too)
  13730.     TA_S *list;
  13731.     int   us_too;
  13732. {
  13733.     ADDRESS *a, *b;
  13734.     TA_S    *ta, *tb;
  13735.     int eliminated = 0;
  13736.  
  13737.     /* toss dupes */
  13738.     for(ta = list; ta; ta = ta->next){
  13739.  
  13740.     if(ta->skip_it) /* already tossed */
  13741.       continue;
  13742.  
  13743.     a = ta->addr;
  13744.  
  13745.     /* Check addr "a" versus tail of the list */
  13746.     for(tb = ta->next; tb; tb = tb->next){
  13747.         b = tb->addr;
  13748.         if(dup_addrs(a, b)){
  13749.         if(ta->checked || !(tb->checked)){
  13750.             /* throw out b */
  13751.             if(tb->checked)
  13752.               eliminated++;
  13753.  
  13754.             tb->skip_it = 1;
  13755.         }
  13756.         else{ /* tb->checked */
  13757.             /* throw out a */
  13758.             ta->skip_it = 1;
  13759.             break;
  13760.         }
  13761.         }
  13762.     }
  13763.     }
  13764.  
  13765.     if(us_too){
  13766.     /* check whether all remaining addrs are us */
  13767.     for(ta = list; ta; ta = ta->next){
  13768.  
  13769.         if(ta->skip_it) /* already tossed */
  13770.           continue;
  13771.  
  13772.         a = ta->addr;
  13773.  
  13774.         if(!address_is_us(a, ps_global))
  13775.           break;
  13776.     }
  13777.  
  13778.     /* if at least one address that isn't us, remove us from the list */
  13779.     if(ta){
  13780.         for(ta = list; ta; ta = ta->next){
  13781.  
  13782.         if(ta->skip_it) /* already tossed */
  13783.           continue;
  13784.  
  13785.         a = ta->addr;
  13786.  
  13787.         if(address_is_us(a, ps_global)){
  13788.             if(ta->checked)
  13789.               eliminated++;
  13790.  
  13791.             /* throw out a */
  13792.             ta->skip_it = 1;
  13793.         }
  13794.         }
  13795.     }
  13796.     }
  13797.  
  13798.     return(eliminated);
  13799. }
  13800.  
  13801.  
  13802. /*
  13803.  * Returns 1 if x and y match, 0 if not.
  13804.  */
  13805. int
  13806. dup_addrs(x, y)
  13807.     ADDRESS *x, *y;
  13808. {
  13809.     return(x && y && strucmp(x->mailbox, y->mailbox) == 0
  13810.            &&  strucmp(x->host, y->host) == 0);
  13811. }
  13812.  
  13813.  
  13814. /*
  13815.  * "Take" address(es) from current active entry and put in any address book
  13816.  *
  13817.  * Args: abook        -- Address book handle
  13818.  *       cur_line     -- The current line position (in global display list)
  13819.  *             of cursor
  13820.  *
  13821.  * Result: The entry is added to one of the address books.
  13822.  */     
  13823. void
  13824. internal_take(abook, cur_line)
  13825.     AdrBk *abook;
  13826.     long   cur_line;
  13827. {
  13828.     ADDRESS       *addr, *a, *a2;
  13829.     int            how_many_selected;
  13830.     TA_S          *current;
  13831.     AdrBk_Entry   *abe;
  13832.     AddrScrn_Disp *dl;
  13833.     char          *fcc, *comment, *fullname, *nickname;
  13834.     TakeAddrScreenMode mode;
  13835.  
  13836.     dprint(2, (debugfile, "\n - taking address from address book - \n"));
  13837.  
  13838.     dl  = dlist(cur_line);
  13839.     abe = ae(cur_line);
  13840.     if(!dl || !abe)
  13841.       return;
  13842.  
  13843.     fcc      = (abe->fcc && abe->fcc[0]) ? abe->fcc : NULL;
  13844.     comment  = (abe->extra && abe->extra[0]) ? abe->extra : NULL;
  13845.     fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL;
  13846.     nickname = (abe->nickname && abe->nickname[0]) ? abe->nickname : NULL;
  13847.     how_many_selected = 0;
  13848.     current = NULL;
  13849.  
  13850.     addr = abe_to_address(abe, dl, abook, &how_many_selected);
  13851.     if(!addr)
  13852.       return;
  13853.  
  13854.     switch(dl->type){
  13855.       case Simple:
  13856.     mode = SingleMode;
  13857.     current = fill_in_ta(¤t, addr, 0, (char *)NULL);
  13858.         break;
  13859.  
  13860.       case ListEnt:
  13861.     mode = SingleMode;
  13862.     current = fill_in_ta(¤t, addr, 0, (char *)NULL);
  13863.     fcc = NULL;
  13864.     comment = NULL;
  13865.     fullname = NULL;
  13866.     nickname = NULL;
  13867.         break;
  13868.  
  13869.       case ListHead:
  13870.     mode = ListMode;
  13871.     for(a = addr; a; a = a->next){
  13872.         a2 = copyaddr(a);
  13873.         current = fill_in_ta(¤t, a2, 1, (char *)NULL);
  13874.         if(a2)
  13875.           mail_free_address(&a2);
  13876.     }
  13877.  
  13878.         break;
  13879.       
  13880.       default:
  13881.     dprint(2,
  13882.         (debugfile, "default case in internal_take, shouldn't happen\n"));
  13883.     return;
  13884.     } 
  13885.  
  13886.     if(addr)
  13887.       mail_free_address(&addr);
  13888.  
  13889.     current = first_sel_taline(current);
  13890.  
  13891.     how_many_selected -= eliminate_dups_but_not_us(current);
  13892.     current = first_sel_taline(current);
  13893.     if(current){
  13894.     if(fullname && *fullname)
  13895.       current->fullname = cpystr(fullname);
  13896.  
  13897.     if(fcc && *fcc)
  13898.       current->fcc = cpystr(fcc);
  13899.  
  13900.     if(comment && *comment)
  13901.       current->comment = cpystr(comment);
  13902.  
  13903.     if(nickname && *nickname)
  13904.       current->nickname = cpystr(nickname);
  13905.     }
  13906.  
  13907.     takeaddr_screen(ps_global, current, how_many_selected, mode);
  13908. }
  13909.  
  13910.  
  13911. /*
  13912.  * Add entries specified by system administrator.  If the nickname already
  13913.  * exists, it is not touched.
  13914.  */
  13915. void
  13916. add_forced_entries(abook)
  13917.     AdrBk *abook;
  13918. {
  13919.     AdrBk_Entry *abe;
  13920.     char *nickname, *fullname, *address;
  13921.     char *end_of_nick, *end_of_full, **t;
  13922.  
  13923.  
  13924.     if(!ps_global->VAR_FORCED_ABOOK_ENTRY ||
  13925.        !ps_global->VAR_FORCED_ABOOK_ENTRY[0] ||
  13926.        !ps_global->VAR_FORCED_ABOOK_ENTRY[0][0])
  13927.     return;
  13928.  
  13929.     for(t = ps_global->VAR_FORCED_ABOOK_ENTRY; *t != NULL; t++){
  13930.     nickname = *t;
  13931.  
  13932.     /*
  13933.      * syntax for each element is
  13934.      * nick[whitespace]|[whitespace]Fullname[WS]|[WS]Address
  13935.      */
  13936.  
  13937.     /* find end of nickname */
  13938.     end_of_nick = nickname;
  13939.     while(*end_of_nick
  13940.           && !isspace((unsigned char)*end_of_nick)
  13941.           && *end_of_nick != '|')
  13942.       end_of_nick++;
  13943.  
  13944.     /* find the pipe character between nickname and fullname */
  13945.     fullname = end_of_nick;
  13946.     while(*fullname && *fullname != '|')
  13947.       fullname++;
  13948.     
  13949.     if(*fullname)
  13950.       fullname++;
  13951.  
  13952.     *end_of_nick = '\0';
  13953.     abe = adrbk_lookup_by_nick(abook, nickname, NULL);
  13954.  
  13955.     if(!abe){  /* If it isn't there, add it */
  13956.  
  13957.         /* skip whitespace before fullname */
  13958.         while(*fullname && isspace((unsigned char)*fullname))
  13959.           fullname++;
  13960.  
  13961.         /* find the pipe character between fullname and address */
  13962.         end_of_full = fullname;
  13963.         while(*end_of_full && *end_of_full != '|')
  13964.           end_of_full++;
  13965.  
  13966.         if(!*end_of_full){
  13967.         dprint(2, (debugfile,
  13968.             "missing | in forced-abook-entry \"%s\"\n", nickname));
  13969.         continue;
  13970.         }
  13971.  
  13972.         address = end_of_full + 1;
  13973.  
  13974.         /* skip whitespace before address */
  13975.         while(*address && isspace((unsigned char)*address))
  13976.           address++;
  13977.  
  13978.         if(*address == '('){
  13979.         dprint(2, (debugfile,
  13980.            "no lists allowed in forced-abook-entry \"%s\"\n", address));
  13981.         continue;
  13982.         }
  13983.  
  13984.         /* go back and remove trailing white space from fullname */
  13985.         while(*end_of_full == '|' || isspace((unsigned char)*end_of_full)){
  13986.         *end_of_full = '\0';
  13987.         end_of_full--;
  13988.         }
  13989.  
  13990.         (void)adrbk_add(abook,
  13991.                NO_NEXT,
  13992.                nickname,
  13993.                fullname,
  13994.                address,
  13995.                NULL,
  13996.                NULL,
  13997.                Single,
  13998.                (adrbk_cntr_t *)NULL,
  13999.                (int *)NULL);
  14000.     }
  14001.     }
  14002. }
  14003.  
  14004.  
  14005.  
  14006. #ifdef    _WINDOWS
  14007. /*
  14008.  * addr_scroll_up - adjust the global screen state struct such that pine's
  14009.  *            window on the data is shifted DOWN (i.e., the data's
  14010.  *            scrolled up).
  14011.  */
  14012. int
  14013. addr_scroll_up(count)
  14014.     long count;
  14015. {
  14016.     if(count < 0)
  14017.     return(addr_scroll_down(-count));
  14018.     else if(count){
  14019.     long i;
  14020.  
  14021.     for(i = count; i && as.top_ent + 1 < as.last_ent; i--, as.top_ent++)
  14022.       ;
  14023.  
  14024.     as.cur_row = ((i = as.cur_row - (count - i)) < 0) ? 0 : i;
  14025.     while(!line_is_selectable(as.top_ent + as.cur_row))
  14026.       as.cur_row++;
  14027.  
  14028.     as.old_cur_row = as.cur_row;
  14029.     }
  14030.  
  14031.     return(1);
  14032. }
  14033.  
  14034.  
  14035. /*
  14036.  * addr_scroll_up - adjust the global screen state struct such that pine's
  14037.  *            window on the data is shifted UP (i.e., the data's
  14038.  *            scrolled down).
  14039.  */
  14040. int
  14041. addr_scroll_down(count)
  14042.     long count;
  14043. {
  14044.     if(count < 0)
  14045.       return(addr_scroll_up(-count));
  14046.     else if(count){
  14047.     long i;
  14048.  
  14049.     for(i = count; i && as.top_ent; i--, as.top_ent--)
  14050.       ;
  14051.  
  14052.     as.cur_row = ((i = as.cur_row + (count - i)) < as.l_p_page - 1)
  14053.                ? i : (as.l_p_page - 1);
  14054.     while(!line_is_selectable(as.top_ent + as.cur_row))
  14055.       as.cur_row--;
  14056.  
  14057.     as.old_cur_row = as.cur_row;
  14058.     }
  14059.  
  14060.     return(1);
  14061. }
  14062.  
  14063.  
  14064. /*
  14065.  * addr_scroll_to_pos - scroll the address book data in pine's window such
  14066.  *            tthat the given "line" is at the top of the page.
  14067.  */
  14068. int
  14069. addr_scroll_to_pos(line)
  14070.     long line;
  14071. {
  14072.     return(addr_scroll_up(line - as.top_ent));
  14073. }
  14074.  
  14075.  
  14076. /*----------------------------------------------------------------------
  14077.      MSWin scroll callback.  Called during scroll message processing.
  14078.          
  14079.  
  14080.  
  14081.   Args: cmd - what type of scroll operation.
  14082.     scroll_pos - parameter for operation.  
  14083.             used as position for SCROLL_TO operation.
  14084.  
  14085.   Returns: TRUE - did the scroll operation.
  14086.        FALSE - was not able to do the scroll operation.
  14087.  ----*/
  14088. int
  14089. addr_scroll_callback (cmd, scroll_pos)
  14090.     int     cmd;
  14091.     long scroll_pos;
  14092. {
  14093.     int paint = TRUE;
  14094.     
  14095.     switch (cmd) {
  14096.       case MSWIN_KEY_SCROLLUPLINE:
  14097.     paint = addr_scroll_down (1);
  14098.     break;
  14099.  
  14100.       case MSWIN_KEY_SCROLLDOWNLINE:
  14101.     paint = addr_scroll_up (1);
  14102.     break;
  14103.  
  14104.       case MSWIN_KEY_SCROLLUPPAGE:
  14105.     paint = addr_scroll_down (as.l_p_page);
  14106.     break;
  14107.  
  14108.       case MSWIN_KEY_SCROLLDOWNPAGE:
  14109.     paint = addr_scroll_up (as.l_p_page);
  14110.     break;
  14111.  
  14112.       case MSWIN_KEY_SCROLLTO:
  14113.     paint = addr_scroll_to_pos (scroll_pos);
  14114.     break;
  14115.     }
  14116.  
  14117.     if(paint)
  14118.       display_book(0, as.cur_row, -1, 1, (Pos *)NULL);
  14119.  
  14120.     return(paint);
  14121. }
  14122. #endif    /* _WINDOWS */
  14123.